//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//


#include "precomp.h"
#ifdef RIL_LAST_ERROR
#include "ccoreutl.h"
#endif

#include "resource.h"
extern HANDLE g_hAckSMSEvent;
extern BOOL g_fCallWaiting;
extern BOOL g_fEmergencyCall;
extern BOOL g_fReportLocationUpdateAsRegistrationState;
extern RILCALLINFO g_CallInfo[MAX_NUMBERCALLS];
extern INT g_iNrCallInfo;
extern CRITICAL_SECTION g_CallInfoSection;
extern UINT g_iPPLMNBufferPos;
extern DWORD g_rsiused;
extern BOOL g_bCINDIsActive;
extern UINT g_iLastRSSI;
extern UINT g_iLastSignal;
extern DWORD g_dwSIMLockedState;
extern DWORD g_LastATD;
extern DWORD g_dwLastCUSSDCmd;
extern BOOL g_bLastCmdPIN2;
extern HANDLE hInstance;
extern DWORD g_iCurrentSIMStatus;


UINT g_dwCEER_LocID;       // AT+CEER resonses
UINT g_dwCEER_reason;
UINT g_dwCEER_ssRel;
#if defined(RIL_SYSTEMTYPE_EDGE)
UINT g_dwLastEDGEStatus = 0;       // last reported status
UINT g_dwEDGEStatus;       // AT^SMONG response
#endif

//
// CME error mapping table
//
static const ERRORMAP g_rgemCMEErrors[] =
{
    { RIL_E_PHONEFAILURE,        0},
    { RIL_E_NOCONNECTION,        1},
    { RIL_E_LINKRESERVED,        2},
    { RIL_E_OPNOTALLOWED,        3},
    { RIL_E_OPNOTSUPPORTED,      4},
    { RIL_E_PHSIMPINREQUIRED,    5},
    { RIL_E_PHFSIMPINREQUIRED,   6},
    { RIL_E_PHFSIMPUKREQUIRED,   7},
    { RIL_E_SIMNOTINSERTED,     10},
    { RIL_E_SIMPINREQUIRED,     11},
    { RIL_E_SIMPUKREQUIRED,     12},
    { RIL_E_SIMFAILURE,         13},
    { RIL_E_SIMBUSY,            14},
    { RIL_E_SIMWRONG,           15},
    { RIL_E_INCORRECTPASSWORD,  16},
    { RIL_E_SIMPIN2REQUIRED,    17},
    { RIL_E_SIMPUK2REQUIRED,    18},
    { RIL_E_MEMORYFULL,         20},
    { RIL_E_INVALIDINDEX,       21},
    { RIL_E_NOTFOUND,           22},
    { RIL_E_MEMORYFAILURE,      23},
    { RIL_E_TEXTSTRINGTOOLONG,  24},
    { RIL_E_INVALIDTEXTSTRING,  25},
    { RIL_E_DIALSTRINGTOOLONG,  26},
    { RIL_E_INVALIDDIALSTRING,  27},
    { RIL_E_NONETWORKSVC,       30},
    { RIL_E_NETWORKTIMEOUT,     31},
    { RIL_E_EMERGENCYONLY,      32},
    { RIL_E_NETWKPINREQUIRED,   40},
    { RIL_E_NETWKPUKREQUIRED,   41},
    { RIL_E_SUBSETPINREQUIRED,  42},
    { RIL_E_SUBSETPUKREQUIRED,  43},
    { RIL_E_SVCPINREQUIRED,     44},
    { RIL_E_SVCPUKREQUIRED,     45},
    { RIL_E_CORPPINREQUIRED,    46},
    { RIL_E_CORPPUKREQUIRED,    47},
// Some of these error messages are not supported by every build.
#if defined(RIL_E_ILLEGALMS)
    { RIL_E_ILLEGALMS,          104},
#endif
#if defined(RIL_E_ILLEGALME)
    { RIL_E_ILLEGALME,          106},
#endif
#if defined(RIL_E_GPRSSERVICENOTALLOWED)
    { RIL_E_GPRSSERVICENOTALLOWED, 107},
#endif
#if defined(RIL_E_PLMNNOTALLOWED)
    { RIL_E_PLMNNOTALLOWED,     111},
#endif
#if defined(RIL_E_LOCATIONAREANOTALLOWED)
    { RIL_E_LOCATIONAREANOTALLOWED, 112},
#endif
#if defined(RIL_E_ROAMINGNOTALLOWEDINTHISLOCATIONAREA)
    { RIL_E_ROAMINGNOTALLOWEDINTHISLOCATIONAREA, 113},
#endif
    { RIL_E_SVCUNSUPPORTED,     132},
    { RIL_E_SVCNOTSUBSCRIBED,   133},
    { RIL_E_NETWKTEMPFAILURE,   134},
#if defined(RIL_E_PDPAUTHENTICATIONFAILURE)
    { RIL_E_PDPAUTHENTICATIONFAILURE, 149},
#endif
#if defined(RIL_E_INVALIDMOBILECLASS)
    { RIL_E_INVALIDMOBILECLASS, 150},
#endif
    { RIL_E_NETWKTEMPFAILURE,   256},
    { RIL_E_DESTINATIONBARRED,  257},
    { RIL_E_NOTREADY,           258},
    { RIL_E_CANCELLED,          259},
    { RIL_E_INVALIDDIALSTRING,  260},
    { RIL_E_SVCUNSUPPORTED,     261}, // Correct meaning of this error is ss not executed, common reason: service is not supported
    { RIL_E_SIMFAILURE,         262}, // Correct meaning of this error is SIM blocked, but the RIL treats this error as blocked for unknowd reasons
    { RIL_E_NOTREADY,           515},
    { RIL_E_NOTREADY,           767}, // this error can also have the meaning of 'operation not allowed', but usually it means 'ME not ready'
    { RIL_E_SIMBUSY,            769}, // MC75/TC63: this usuallly means 'SIM busy' rather than 'SIM powered down'
    { RIL_E_SIMFAILURE,         770}, // MC75/TC63: SIM permanently blocked and cannot be restored (not even by the provider)
};
#define NUM_CMEERRORS       (sizeof(g_rgemCMEErrors) / sizeof(ERRORMAP))


//
// CMS error mapping table
//
static const ERRORMAP g_rgemCMSErrors[] =
{
    { RIL_E_UNASSIGNEDNUMBER,         1},
    { RIL_E_MSGBARREDBYOPERATOR,      8},
    { RIL_E_MSGCALLBARRED,           10},
    { RIL_E_MSGXFERREJECTED,         21},
    { RIL_E_DESTINATIONOUTOFSVC,     27},
    { RIL_E_UNIDENTIFIEDSUBCRIBER,   28},
    { RIL_E_SVCUNSUPPORTED,          29},
    { RIL_E_UNKNOWNSUBSCRIBER,       30},
    { RIL_E_NETWKOUTOFORDER,         38},
    { RIL_E_NETWKTEMPFAILURE,        41},
    { RIL_E_CONGESTION,              42},
    { RIL_E_RESOURCESUNAVAILABLE,    43},
    { RIL_E_SVCNOTSUBSCRIBED,        50},
    { RIL_E_SVCNOTIMPLEMENTED,       69},
    { RIL_E_INVALIDMSGREFERENCE,     81},
    { RIL_E_INVALIDMSG,              95},
    { RIL_E_INVALIDMANDATORYINFO,    96},
    { RIL_E_MSGTYPEUNSUPPORTED,      97},
    { RIL_E_ICOMPATIBLEMSG,          98},
    { RIL_E_INFOELEMENTUNSUPPORTED,  99},
    { RIL_E_PROTOCOLERROR,          111},
    { RIL_E_NETWORKERROR,           127},
    { RIL_E_TELEMATICIWUNSUPPORTED, 128},
    { RIL_E_SMTYPE0UNSUPPORTED,     129},
    { RIL_E_CANTREPLACEMSG,         130},
    { RIL_E_PROTOCOLIDERROR,        143},
    { RIL_E_DCSUNSUPPORTED,         144},
    { RIL_E_MSGCLASSUNSUPPORTED,    145},
    { RIL_E_DCSERROR,               159},
    { RIL_E_CMDCANTBEACTIONED,      160},
    { RIL_E_CMDUNSUPPORTED,         161},
    { RIL_E_CMDERROR,               175},
    { RIL_E_MSGBODYHEADERERROR,     176},
    { RIL_E_SCBUSY,                 192},
    { RIL_E_NOSCSUBSCRIPTION,       193},
    { RIL_E_SCSYSTEMFAILURE,        194},
    { RIL_E_INVALIDADDRESS,         195},
    { RIL_E_DESTINATIONBARRED,      196},
    { RIL_E_REJECTEDDUPLICATE,      197},
    { RIL_E_VPFUNSUPPORTED,         198},
    { RIL_E_VPUNSUPPORTED,          199},
    { RIL_E_SIMMSGSTORAGEFULL,      208},
    { RIL_E_NOSIMMSGSTORAGE,        209},
    { RIL_E_PHONEFAILURE,           210},
    { RIL_E_MEMORYFULL,             211},
    { RIL_E_SIMTOOLKITBUSY,         212},
    { RIL_E_SIMDOWNLOADERROR,       213},
    { RIL_E_PHONEFAILURE,           300},
    { RIL_E_MSGSVCRESERVED,         301},
    { RIL_E_OPNOTALLOWED,           302},
    { RIL_E_OPNOTSUPPORTED,         303},
    { RIL_E_INVALIDMSGPARAM,        304},
    { RIL_E_INVALIDMSGPARAM,        305},
    { RIL_E_SIMNOTINSERTED,         310},
    { RIL_E_SIMPINREQUIRED,         311},
    { RIL_E_PHSIMPINREQUIRED,       312},
    { RIL_E_SIMFAILURE,             313},
    { RIL_E_SIMBUSY,                314},
    { RIL_E_SIMWRONG,               315},
    { RIL_E_SIMPUKREQUIRED,         316},
    { RIL_E_SIMPIN2REQUIRED,        317},
    { RIL_E_SIMPUK2REQUIRED,        318},
    { RIL_E_MEMORYFAILURE,          320},
    { RIL_E_INVALIDINDEX,           321},
    { RIL_E_MEMORYFULL,             322},
    { RIL_E_UNKNOWNSCADDRESS,       330},
    { RIL_E_NONETWORKSVC,           331},
    { RIL_E_NETWORKTIMEOUT,         332},
    { RIL_E_MESSAGINGERROR,         512},
    { RIL_E_MESSAGINGERROR,         513},
    { RIL_E_MESSAGINGERROR,         514},
    { RIL_E_NOTREADY,               515},
};
#define NUM_CMSERRORS       (sizeof(g_rgemCMSErrors) / sizeof(ERRORMAP))

//
// CallerID validities
//
static const DWORD g_rgdwValidities[] =
{
    RIL_REMOTEPARTYINFO_VALID,          // 0
    RIL_REMOTEPARTYINFO_WITHHELD,       // 1
    RIL_REMOTEPARTYINFO_UNAVAILABLE,    // 2
};
#define NUM_VALIDITIES      (sizeof(g_rgdwValidities) / sizeof(DWORD))


//
// Supplementary service data status values
//
static const DWORD g_rgdwSupSvcDataStats[] =
{
    RIL_SUPSVCDATASTATUS_NOINFOREQUIRED,        // 0
    RIL_SUPSVCDATASTATUS_FURTHERINFOREQUIRED,   // 1
    RIL_SUPSVCDATASTATUS_TERMINATED,            // 2
    RIL_SUPSVCDATASTATUS_OTHERCLIENTRESPONDED,  // 3
    RIL_SUPSVCDATASTATUS_UNSUPPORTED,           // 4
    RIL_SUPSVCDATASTATUS_TIMEOUT,               // 5
};
#define NUM_SUPSVCDATASTATS     (sizeof(g_rgdwSupSvcDataStats) / sizeof(DWORD))

#ifdef RIL_ADVANCEDCALLRELEASEINDICATIONS
#ifndef RIL_ADVANCEDUSSDERRORCAUSES
#define RIL_ADVANCEDUSSDERRORCAUSES
#endif
#endif
#ifdef RIL_INDICATE_AOCMAX
#ifndef RIL_ADVANCEDUSSDERRORCAUSES
#define RIL_ADVANCEDUSSDERRORCAUSES
#endif
#endif

//
// USSD error causes
//
#ifdef RIL_ADVANCEDUSSDERRORCAUSES
static const USSDERRORCAUSE g_rgUSSDErrorCausesTable[] =
{
    {35,  0,  34, IDS_USSD_ERROR_SYSTEM_FAILURE},
    {35,  0,  35, IDS_USSD_ERROR_DATA_MISSING},
    {35,  0,  71, IDS_USSD_ERROR_UNKNOWN_ALPHABET},
    {35,  0,  36, IDS_USSD_ERROR_UNEXPECTED_DATA_VALUE},
    {128, 0, 300, IDS_USSD_ERROR_UNRECOGNIZED_COMPONENT},
    {128, 0, 301, IDS_USSD_ERROR_MISTYPED_COMPONENT},
    {128, 0, 302, IDS_USSD_ERROR_BADLY_STRUCTURED_COMPONENT},
    {129, 0, 305, IDS_USSD_ERROR_MISTYPED_PARAMETER},
    {129, 0, 306, IDS_USSD_ERROR_RESOURCE_LIMITATION},
    {129, 0, 307, IDS_USSD_ERROR_INITIATING_RELEASE}
};
#define NUM_USSDERRORCAUSES   (sizeof(g_rgUSSDErrorCausesTable) / sizeof(USSDERRORCAUSE))
#endif

//
// Call release causes
//
#ifdef RIL_ADVANCEDCALLRELEASEINDICATIONS
static const USSDERRORCAUSE g_rgCallReleaseErrorCausesTable[] =
{
    {9,  300,  0, IDS_ADV_CALL_BARRING_INCOMMING_CALL}
};
#define NUM_CALLRELEASEERRORCAUSES   (sizeof(g_rgCallReleaseErrorCausesTable) / sizeof(USSDERRORCAUSE))
#endif

#ifdef RIL_INDICATE_SS_INFORMATION
//
// Supplementyry service indications
//
static const SSINDICATION g_rgCSSIIndicationsTable[] =
{
    {0, IDS_SS_INDICATION_CSSI_0},
    {1, IDS_SS_INDICATION_CSSI_1},
    {2, IDS_SS_INDICATION_CSSI_2},
    {3, IDS_SS_INDICATION_CSSI_3}
};
#define NUM_CSSIINDICATIONSCAUSES   (sizeof(g_rgCSSIIndicationsTable) / sizeof(SSINDICATION))
static const SSINDICATION g_rgCSSUIndicationsTable[] =
{
    {0,  IDS_SS_INDICATION_CSSU_0},
    {10, IDS_SS_INDICATION_CSSU_10}
};
#define NUM_CSSUINDICATIONSCAUSES   (sizeof(g_rgCSSUIndicationsTable) / sizeof(SSINDICATION))
#endif

// These commands don't need a timoeout definition per registry.
#define CEER_TIMEOUT          9000     // Timeout (in ms) for AT+CEER command
#define SMONG_TIMEOUT        10000     // Timeout (in ms) for AT^SMONG command
#define CUSD_CANCEL_TIMEOUT  15000     // Timeout (in ms) for AT+CUSD=2 command

#ifdef RIL_LAST_ERROR
extern DWORD g_dwLastError;
#endif

extern RHA_FUNCTION_LIST RHAFunctionList;
void RHADeactivateVoicePath();

DWORD WINAPI ACMMaxThreadProc(LPVOID);
DWORD WINAPI CancelUSSDThreadProc(LPVOID pVoid);
#ifdef RIL_ADVANCEDUSSDERRORCAUSES
DWORD WINAPI InvestigateUSSDThreadProc(LPVOID pVoid);
#endif
#ifdef RIL_ADVANCEDCALLRELEASEINDICATIONS
DWORD WINAPI InvestigateCallReleaseThreadProc(LPVOID pVoid);
#endif

#ifdef RIL_ENABLE_ALS
UINT           g_dwALSLine = 0;
#endif

//
// Response ctor
//
CResponse::CResponse(COM_PORT_TYPE PortType, CRilHandle *pRilHandle) :
m_PortType(PortType),
m_dwCode(0),
m_fUnsolicited(FALSE),
m_fUnrecognized(FALSE),
m_fPotentialBogusResponse(FALSE),
m_pBlob(NULL),
m_iNrCallInfo(0),
m_cbBlob(0),
m_pRilHandle(pRilHandle)
{
    // TBD_FUNCTION(CResponse::CResponse);
}


//
// copy Response ctor
//
CResponse::CResponse(const CResponse &Rsp) :
m_PortType(Rsp.m_PortType),
m_dwCode(Rsp.m_dwCode),
m_fUnsolicited(Rsp.m_fUnsolicited),
m_fUnrecognized(Rsp.m_fUnrecognized),
m_fPotentialBogusResponse(Rsp.m_fPotentialBogusResponse),
m_pBlob(NULL),
m_iNrCallInfo(0),
m_cbBlob(0),
m_pRilHandle(Rsp.m_pRilHandle)
// CBuffer initialized null.
{
}


//
// Response dtor
//
CResponse::~CResponse()
{
    // TBD_FUNCTION(CResponse::~CResponse);
    DeleteBlob();
}


//
// Append data to the response
//
BOOL CResponse::AppendString(const LPCSTR szString, const UINT cbString, LPCSTR& rszRemainder, UINT& rcbRemainder)
{
    //TBD_FUNCTION(CResponse::AppendString);
    UINT cbNewLength = 0;
    UINT cbOldLength = m_cbLength;
    BOOL fRet = FALSE;

    // Append the new data to the buffer
    if (!Append(szString, cbString))
    {
        goto Error;
    }

    // Parse the data
    if (Parse(cbNewLength))
    {
        TBD_ASSERT(cbNewLength != 0);
        TBD_ASSERT(cbNewLength <= m_cbLength);

        m_cbLength = cbNewLength;
        rszRemainder = szString + cbNewLength - cbOldLength;
        rcbRemainder = cbString - (rszRemainder - szString);
    }
    else
    {
        rszRemainder = NULL;
        rcbRemainder = 0;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::SetBlob(const void* const pBlob, const UINT cbBlob)
{
    //TBD_FUNCTION(CResponse::SetBlob);
    BOOL fRet = FALSE;

    TBD_ASSERT(NULL == m_pBlob && 0 == m_cbBlob);
    DeleteBlob();

    if (cbBlob)
    {
        TBD_ASSERT(NULL != pBlob);

        m_pBlob = AllocBlob(cbBlob);
        if (!m_pBlob)
        {
            // Critically low on memory
            SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
            goto Error;
        }
        memcpy(m_pBlob, pBlob, cbBlob);
    }

    m_cbBlob = cbBlob;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::OKToError(HRESULT hr)
{
    //TBD_FUNCTION(CResponse::OKToError);
    TBD_ASSERT(RIL_RESULT_OK == m_dwCode);
    TBD_ASSERT(FALSE == m_fUnsolicited);
    return MakeError(hr);
}


//
//
//
BOOL CResponse::MakeError(const HRESULT hrError)
{
    //TBD_FUNCTION(CResponse::MakeError);
    // Get rid of the old blob
    DeleteBlob();

    m_fUnsolicited = FALSE;
    m_dwCode = RIL_RESULT_ERROR;
    return SetBlob((void*)&hrError, sizeof(HRESULT));
}


//
// Parse a response
//
BOOL CResponse::Parse(UINT& rcbNewLength)
{
    //TBD_FUNCTION(CResponse::Parse);
    DWORD   i;
    // NULL-terminate the response string temporarily
    m_szData[m_cbLength] = '\0';

    // Remove any leading zeroes caused by the MC45 powering on/off
    if(m_cbLength>1 && m_szData[0] == '\0')
    {
        for(i=0;i<m_cbLength;i++)m_szData[i] = m_szData[i+1];
        m_cbLength--;
    }
    // Remove the PPP packet if received during connecting
    if(m_cbLength>11 && (!strncmp(m_szData, "+CR: ", 5) || !strncmp(m_szData, "CONNECT\r\n", 9)))
    {
        for(i=0;i<(m_cbLength-1);i++)
        {
            if(m_szData[i] == '\r' && m_szData[i+1] != '\n')
            {
                m_szData[i+1] = '\0';
                m_cbLength = i+1;
                break;
            }
        }
    }
    // In case there are any \0, we replace them with ' '.
    for(i=0;i<m_cbLength;i++)
    {
        if (m_szData[i] == 0)
        {
            m_szData[i] = ' ';
            RILRetailTrace((TEXT("RilDrv: Unexpected NULL-Byte in response on %s.\r\n"), g_ComManager.GetDebugName(m_PortType)));
        }
    }

#ifdef TBD_DEBUG
    //TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Accumulated response: %s"), TString(PrintableString(m_szData, m_cbLength)));
#endif // TBD_DEBUG

    RILRetailTrace((TEXT("RilDrv: -----------------------------------------")));
    ExtRILRetailTrace(TEXT("RilDrv: Accumulated response (%s): "), g_ComManager.GetDebugName(m_PortType), m_szData);
    RILRetailTrace((TEXT("RilDrv: -----------------------------------------")));

    return (ParseNotification(rcbNewLength)    ||
            ParseOKOrError(TRUE, rcbNewLength) ||
            ParseOKOrError(FALSE, rcbNewLength));
}

// This function searches for strings of the form "+X", where X is a set of at least 2
// uppercase characters. If found, it sets rszPointer to the beginning of the string,
// and returns the number of characters in the string.
int FindNextResponse(LPCSTR szStart, LPCSTR& rszPointer)
{
    //TBD_FUNCTION(FindNextResponse);
    LPCSTR szPointer = szStart;
    LPCSTR szCmd;
    int RespSize;

    while (1)
    {
        // Find the first '+' character
        szPointer = strchr(szPointer, '+');
        if (!szPointer)
        {
            // Didn't find a +, abort
            return 0;
        }

        // We found a '+', now look for some capital letters
        RespSize=1;
        szCmd = szPointer+1;
        while (1)
        {
            UCHAR NextChar = *szCmd;

            // If we hit a NULL in the middle of parsing, abort
            if (NextChar=='\0')
            {
                return 0;
            }
            // Is it a capital letter?
            else if ((NextChar >= 'A') && (NextChar <= 'Z'))
            {
                RespSize++;
                szCmd++;
            }
            // Is it a ':', which ends a response?
            else if (NextChar == ':')
            {
                RespSize++;
                rszPointer=szPointer;
                return RespSize;
            }
            // It's some other character. Keep looking.
            else
            {
                // Remember to increment the initial pointer so we don't try to
                // reparse the same + character in an infinite loop
                szPointer=szCmd;
                break;
            }
        }
    }
}

//
//
//
BOOL CResponse::ParseOKOrError(const BOOL fOK, UINT& rcbNewLength)
{
    //TBD_FUNCTION(CResponse::ParseOKOrError);
    LPSTR szPointer = m_szData;
    LPCSTR szCode = (fOK ? "OK\r\n" : "ERROR\r\n");
    HRESULT hrError;
    BOOL fRet;

    while (1)
    {
        // First search in the beginning of the response
        fRet = MatchStringBeginning(szPointer, szCode, szPointer);
        if (fRet && '\n' != *szPointer)
        {
            // We found "<code><CR>" not followed by <LF> in the beginning of the response
            break;
        }
        else
        {
            // Now search elsewhere in the response
            fRet = MatchStringAnywhere(szPointer, szCode, szPointer);
            if (!fRet && fOK)
            {
                // We didn't find "<code><CR>"
                break;
            }
            if (!fRet && !fOK)
            {
                // We also have to look for an +CME ERROR: 100, which means the command was
                // aborted in the middle of a response.
                fRet = MatchStringAnywhere(szPointer, "+CME ERROR: 100\r\n", szPointer);
                if (!fRet)
                {
                    // We also didn't find "+CME ERROR: 100<CR>"
                    break;
                }
                else if ('\n' != *szPointer)
                {
                    break;
                }
            }
            else if ('\n' != *szPointer && ('\n' == *(szPointer - (fOK?4:8)) || '\r' == *(szPointer - (fOK?4:8))))
            {
                // We found "<code><CR><LF>"  not followed by <LF> and preceded by <LF> or <CR>
                break;
            }
        }
    }
    if (!fRet && fOK)
    {
        // If that didn't succeed either, see if we got an SMS intermediary prompt
        // we'll treat this as an OK for now, but this will cause the command thread
        // to send down the second part of the command which will be the SMS PDU
        fRet = MatchStringBeginning(m_szData, "\r\n> ", szPointer);
    }
    if (!fRet && fOK)
    {
        // If we couldn't find the numeric OK or ERROR, try looking for the special OK response
        fRet = MatchStringAnywhere(m_szData, "\r\nOK\r\n", szPointer);
    }

    if (fRet)
    {
        m_fUnsolicited = FALSE;
        m_dwCode = (fOK ? RIL_RESULT_OK : RIL_RESULT_ERROR);
        rcbNewLength = szPointer - m_szData;

        if (!fOK)
        {
            // For error responses, we have additional info -- error code
            hrError = E_FAIL;
            if (!SetBlob((void*)&hrError, sizeof(HRESULT)))
            {
                fRet = FALSE;
                goto Error;
            }
        }
    }

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::ParseRadioSysstart(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseRadioSysstart);

    RILEQUIPMENTSTATE res;
    BOOL              fFlightMode = FALSE;

    if (MatchStringAnywhere(rszPointer, "AIRPLANE MODE", rszPointer))
    {
        fFlightMode = TRUE;
    }
    if (!FindRspPostfix(rszPointer, rszPointer))
    {
        return FALSE;
    }

    // This URC will show up on every multiplexer channel, we handle only this on the urc port.
    if (m_PortType == URC_PORT)
    {
        if (g_PowerManager.SysstartFinished(&res, fFlightMode))
        {
            m_fUnsolicited = TRUE;

            m_dwCode = RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED;
            if (!SetBlob((void*)&res, sizeof(RILEQUIPMENTSTATE)))
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}


//
//
//
BOOL CResponse::ParseRadioShutdown(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseRadioShutdown);

    RILEQUIPMENTSTATE res;

    if (!g_PowerManager.ShutdownFinished(&res))
    {
        // something went utterly wrong
        g_PowerManager.ForcePowerOff();
        return FALSE;
    }
    else
    {
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED;
        if (!SetBlob((void*)&res, sizeof(RILEQUIPMENTSTATE)))
        {
            return FALSE;
        }
    }

    return TRUE;
}


struct RESPONSEV1VSV0 {
    char szV1String[20];
    UINT uiV0Code;
};

BOOL CResponse::SearchV1Response(LPCSTR& szPointer, UINT *uiRetCode)
{
    BOOL bFound = FALSE;
    const RESPONSEV1VSV0 V0vsV1[] = {
        {"RING",         2 },  // Ring detected
        {"NO CARRIER",   3 },  // Link not established or disconnected
        {"NO DIALTONE",  6 },  // No dial tone, dialling impossible, wrong mode
        {"BUSY",         7 },  // Remote station busy
        {"NO ANSWER",    8 },  // -- not supported by MC45
        {"CONNECT 2400/RLP",  47}, // Link with 2400 bps and Radio Link Protocol
        {"CONNECT 4800/RLP",  48}, // Link with 4800 bps and Radio Link Protocol
        {"CONNECT 9600/RLP",  49}, // Link with 9600 bps and Radio Link Protocol
        {"CONNECT 14400/RLP", 50}, // Link with 14400 bps and Radio Link Protocol
        {"CONNECT",      1 }   // Link established
    };
    UINT i;

    for (i=0; i<(sizeof(V0vsV1) / sizeof(RESPONSEV1VSV0)); i++)
    {
        if (MatchStringBeginning(szPointer, V0vsV1[i].szV1String, szPointer))
        {
            *uiRetCode = V0vsV1[i].uiV0Code;
            bFound = TRUE;
            break;
        }
    }
    return bFound;
}

//
//
//
BOOL CResponse::ParseNotification(UINT& rcbNewLength)
{
    //TBD_FUNCTION(CResponse::ParseNotification);
    LPSTR szPointer = m_szData;
    BOOL fExpectCRLF = TRUE;
    BOOL fExpectNothing = FALSE;
    BOOL fSuppressLogging = FALSE;
    BOOL fRet = FALSE;
    UINT uiV0Code;

    if (MatchStringBeginning(szPointer, "+CCWA: ", szPointer))
    {
        // Caller ID notification
        if (!ParseCallWaitingInfo(szPointer))
        {
            goto Error;
        }
        g_fCallWaiting = TRUE;

    }
    else
    {

    // Parse "<prefix>"
    if (!ParseRspPrefix(szPointer, szPointer))
    {
        goto Error;
    }
    if (MatchStringBeginning(szPointer, "+++", szPointer) &&
        MatchStringAnywhere(szPointer, "NO CARRIER", szPointer))
    {
        // Special case for "+++" followed by 3(NO CARRIER)
        m_fUnsolicited = FALSE;
        m_dwCode = RIL_RESULT_NOCARRIER;
        fExpectCRLF = TRUE;
    }
    else if (SearchV1Response(szPointer, &uiV0Code))
    {
        if (!ParseV25Response(uiV0Code))
        {
            goto Error;
        }
        fExpectCRLF = TRUE;
    }
    else if (MatchStringBeginning(szPointer, "+CIEV: ", szPointer))
    {
        if (!ParseIndicatorEventValue(szPointer))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "^SCKS: ", szPointer))
    {
        if (!ParseSIMCardStatus(szPointer))
        {
           goto Error;
        }
    }
#ifdef RIL_ENABLE_ALS
    else if (MatchStringBeginning(szPointer, "^SALS: ", szPointer))
    {
        if (!ParseALSStatusStatus(szPointer))
        {
           goto Error;
        }
    }
#endif
    else if (MatchStringBeginning(szPointer, "+CREG: ", szPointer))
    {
        if (!ParseRegistrationStatus(szPointer,RIL_NOTIFY_REGSTATUSCHANGED))
        {
            goto Error;
        }
        // In case we did register the advanced registration status, we have to switch
        // back to the normal mode as soon we are registered to a network (or, at least,
        // start attempting).
        if (g_fReportLocationUpdateAsRegistrationState && (g_dwRegStatus==RIL_REGSTAT_HOME || g_dwRegStatus==RIL_REGSTAT_ROAMING || g_dwRegStatus==RIL_REGSTAT_ATTEMPTING))
        {
            g_fReportLocationUpdateAsRegistrationState = FALSE;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CGREG: ", szPointer))
    {
        if (!ParseRegistrationStatus(szPointer,RIL_NOTIFY_GPRSREGSTATUSCHANGED))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CRING: ", szPointer))
    {
        DWORD dwCallType = RIL_CALLTYPE_UNKNOWN;
        
        // Ring notification
        if (!ParseExtRing(szPointer, &dwCallType))
        {
            goto Error;
        }
        EnterCriticalSection(&g_CallInfoSection);
        if (g_iNrCallInfo == 0)
        {
            // Rare timing condition: the +CRING-URC usually shows up 50ms before the corresponding ^SLCC-URC.
            // We treat this case like the ATD, since there are also some ms between the OK and the ^SLCC-URC.
            g_LastATD = GetTickCount();

            // Workaround for MC75 problem: in Rel3 the ^SLCC URC sometimes doesn't show up after +CRING,
            // so we have to create a dummy entry. If the ^SLCC URC does show up on other modules, this entry
            // will be overwritten with the correct one.
            memset(&g_CallInfo[0], 0x00, sizeof(RILCALLINFO));
            g_CallInfo[0].cbSize  = sizeof(RILCALLINFO);
            g_CallInfo[0].raAddress.cbSize = sizeof(RILADDRESS);
            g_CallInfo[0].dwID = 1;
            g_CallInfo[0].dwParams |= RIL_PARAM_CI_ID;
            g_CallInfo[0].dwDirection = RIL_CALLDIR_INCOMING;
            g_CallInfo[0].dwParams |= RIL_PARAM_CI_DIRECTION;
            g_CallInfo[0].dwStatus = RIL_CALLSTAT_INCOMING;
            g_CallInfo[0].dwParams |= RIL_PARAM_CI_STATUS;
            g_CallInfo[0].dwType = dwCallType;
            g_CallInfo[0].dwParams |= RIL_PARAM_CI_TYPE;
            g_CallInfo[0].dwMultiparty = RIL_CALL_SINGLEPARTY;
            g_CallInfo[0].dwParams |= RIL_PARAM_CI_TYPE;
            g_iNrCallInfo = 1;
        }
        LeaveCriticalSection(&g_CallInfoSection);
    }
    else if (MatchStringBeginning(szPointer, "+CR: ", szPointer))
    {
        // Connection service notification
        if (!ParseServiceInfo(szPointer))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CLIP: ", szPointer))
    {
        // Caller ID notification
        if (!ParseRemotePartyInfo(szPointer, TRUE))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+COLP: \r\n", szPointer) || MatchStringBeginning(szPointer, "+COLP: \",128\r\n", szPointer))
    {
        // We just ignore these empty '+COLP:' URCs which occasinally
        // show up on the MC75 and have no meaning.
        if (strlen(m_pRilHandle->GetLastDialedNumber())!=0)
        {
            RILREMOTEPARTYINFO rrpi ; 

            memset(&rrpi,0,sizeof(rrpi)); // zero struct
            rrpi.cbSize = sizeof(RILREMOTEPARTYINFO);
            rrpi.dwParams = RIL_PARAM_RPI_ADDRESS;
            rrpi.raAddress.dwType = RIL_CALLTYPE_UNKNOWN;
            wsprintf(rrpi.raAddress.wszAddress, L"%S", m_pRilHandle->GetLastDialedNumber());
            rrpi.raAddress.dwType = RIL_ADDRTYPE_UNKNOWN;
            rrpi.raAddress.dwParams = RIL_PARAM_A_TYPE;
            
            m_dwCode = RIL_NOTIFY_DIALEDID;
            if (!SetBlob((void*)&rrpi, sizeof(RILREMOTEPARTYINFO)))
            {
                goto Error;
            }
#ifdef RIL_INDICATE_COLP
            WCHAR wszText[MAX_RESOURCE_STRING_LEN];
            CNotificationData* pnd = NULL;
            RILSUPSERVICEDATA *prssd;
            INT cbStruct;

            if (LoadString((HINSTANCE)hInstance, IDS_INDICATION_COLP, wszText, MAX_RESOURCE_STRING_LEN) > 0)
            {
                WCHAR wszOut[MAX_RESOURCE_STRING_LEN];

                wsprintf(wszOut, wszText, m_pRilHandle->GetLastDialedNumber());
                cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszOut) * sizeof(WCHAR)) + sizeof(WCHAR);
                prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
                memcpy(prssd->pbData, (BYTE *)wszOut, (wcslen(wszOut) * sizeof(WCHAR)) + sizeof(WCHAR));
                prssd->dwParams = RIL_PARAM_SSDI_DATA;
                prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                prssd->cbSize = cbStruct;
                pnd = new CNotificationData;
                if (pnd)
                {
                    pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize);
                    m_pRilHandle->BroadcastNotification(pnd);
                }
            }
#endif
            m_pRilHandle->DeleteLastDialedNumber();
        }
        m_fUnsolicited = TRUE;
        szPointer = szPointer - 2;
    }
    else if (MatchStringBeginning(szPointer, "+COLP: ", szPointer))
    {
        // Caller ID notification
        if (!ParseRemotePartyInfo(szPointer, FALSE))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CCWA: ", szPointer))
    {
        // Caller ID notification
        if (!ParseCallWaitingInfo(szPointer))
        {
            goto Error;
        }
        g_fCallWaiting = TRUE;
    }
    else if (MatchStringBeginning(szPointer, "^SCWA:\r\n", szPointer))
    {
        // This URC should never show up: it appears, when a call was waiting, while the
        // module was not ready, and the caller hung up, before out radio module was
        // able to indicate this call. Since it is now too late to do anything about it,
        // there is no need to indicate this event.
        m_fUnsolicited = TRUE;
        szPointer = szPointer - 2;
    }
#ifdef SIEMENS_MAGNETO
    else if (MatchStringBeginning(szPointer, "+CSSU: ", szPointer))
    {
        // Unsolicited Supplementary Service notification
        if (!ParseUnsolicitedSSInfo(szPointer))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CSSI: ", szPointer))
    {
        // Intermediate Supplementary Service notification
        if (!ParseIntermediateSSInfo(szPointer))
        {
            goto Error;
        }
    }
#endif
    else if (MatchStringBeginning(szPointer, "+CCCM: ", szPointer))
    {
        // Current Call Meter notification
        if (!ParseCallMeter(szPointer))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CMT: ", szPointer))
    {
        // SMS-DELIVER notification
        char szAlpha[MAXLENGTH_DESCRIPTION];
        (void)ParseString(szPointer, szAlpha, MAXLENGTH_DESCRIPTION, szPointer);
        if (!MatchStringBeginning(szPointer, ",", szPointer) ||
            !ParseMessage(szPointer, RIL_NOTIFY_MESSAGE))
        {
            goto Error;
        }
        SetEvent(g_hAckSMSEvent);
    }
    else if (MatchStringBeginning(szPointer, "+CBM: ", szPointer))
    {
        // Broadcast notification
        if (!ParseMessage(szPointer, RIL_NOTIFY_BCMESSAGE))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CDS: ", szPointer))
    {
        // SMS-STATUS-REPORT notification
        if (!ParseMessage(szPointer, RIL_NOTIFY_STATUSMESSAGE))
        {
            goto Error;
        }
        SetEvent(g_hAckSMSEvent);
    }
    else if (MatchStringBeginning(szPointer, "+CMTI: ", szPointer))
    {
        if (!ParseMessageInSim(szPointer, RIL_NOTIFY_MESSAGE_IN_SIM, m_pRilHandle->IsSMSURCInitPhase()))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CBMI: ", szPointer))
    {
        if (!ParseMessageInSim(szPointer, RIL_NOTIFY_BCMESSAGE_IN_SIM))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CDSI: ", szPointer))
    {
        if (!ParseMessageInSim(szPointer, RIL_NOTIFY_STATUSMESSAGE_IN_SIM))
        {
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+CUSD: ", szPointer))
    {
        // USSD notification
        if (!ParseUSSDInfo(szPointer, fExpectNothing))
        {
            goto Error;
        }
        fSuppressLogging = TRUE;    // USSD content should not be recorded
        g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_USSDRECEIVED);
    }
    else if (MatchStringBeginning(szPointer, "^SLCC: ", szPointer))
    {
        // Handle SCCL Call List
        if (!ParseCallListNotification(szPointer))
        {
            goto Error;
        }

        // We don't use CLIP on the MC75, so we simulate it with data from the ^SLCC-URC.
        if (g_dwModemType == MODEMTYPE_SIEMENS_MC75 && g_iNrCallInfo == 1 && m_pRilHandle->IsSimulatedCLIPActive())
        {
            CNotificationData *pnd;
            RILREMOTEPARTYINFO ri;

            memset(&ri, 0, sizeof(RILREMOTEPARTYINFO));
            EnterCriticalSection(&g_CallInfoSection);
            if (g_iNrCallInfo == 1)
            {
                if (g_CallInfo[0].dwStatus==RIL_CALLSTAT_INCOMING && (g_CallInfo[0].dwParams&RIL_PARAM_CI_ADDRESS)!=0)
                {
                    ri.cbSize = sizeof(RILREMOTEPARTYINFO);
                    ri.dwParams = RIL_PARAM_RPI_ADDRESS;
                    memcpy(&ri.raAddress, &g_CallInfo[0].raAddress, sizeof(RILADDRESS));
                }
            }
            LeaveCriticalSection(&g_CallInfoSection);
            if (ri.cbSize != 0)
            {
                pnd = new CNotificationData;
                if (pnd)
                {
                    pnd->InitFromRealBlob(RIL_NOTIFY_CALLERID, (void*)&ri, ri.cbSize);
                    m_pRilHandle->BroadcastNotification(pnd);
                }
            }
        }
    }
    else if (MatchStringBeginning(szPointer, "^SSTN: ", szPointer))
    {
        // Unhandled SIM Toolkit command notification
        if (!m_pRilHandle->GetSimToolkit()->ParseNotification(szPointer))
        {
            goto Error;
        }
        // Don't set m_fUnsolicited = TRUE because SAT is handled separetely in it's own thread!
    }
    else if (MatchStringBeginning(szPointer, "^SYSSTART", szPointer))
    {
        if (!ParseRadioSysstart(szPointer))
        {
            goto Error;
        }
        rcbNewLength = szPointer - m_szData; // Skip past the ^SYSSTART URC
        fRet = TRUE;
        goto Error;
    }
    else if (MatchStringBeginning(szPointer, "^SHUTDOWN\r\n", szPointer))
    {
        if (!ParseRadioShutdown(szPointer))
        {
            goto Error;
        }
        rcbNewLength = szPointer - m_szData; // Skip past the ^SHUTDOWN URC
        fRet = TRUE;
        goto Error;
    }
    else if (MatchStringBeginning(szPointer, "^SBC: ", szPointer))
    {
        // The ^SBC: URC anounces am upcomming emergency shoutdown due to undervoltage.
        // The best way to deal with this is gracefull shutdown of the radio module.
        // This URC will show up on every multiplexer channel. To prevent to generate
        // multiple shutdown events, we will only look for the one on the URC channel.
        if (FindRspPostfix(szPointer, szPointer))
        {
            if (m_PortType == URC_PORT)
            {
                m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_UNDERVOLTAGE);
                g_PowerManager.EnterEquipmentState(NULL, RIL_EQSTATE_MINIMUM);
            }
        }
        m_fUnsolicited = FALSE;
        rcbNewLength = szPointer - m_szData; // Skip past the ^SBC URC
        fRet = TRUE;
        goto Error;
    }
    else if (MatchStringBeginning(szPointer, "^SRSA: ", szPointer))
    {
        // RSA notification
        if (!m_pRilHandle->GetRsa()->ParseNotification(szPointer, m_PortType))
        {
            goto Error;
        }
        // Don't set m_fUnsolicited = TRUE because RSA is handled separetely in it's own thread!
    }
    else if (MatchStringBeginning(szPointer, "^SCTM_A: ", szPointer) || MatchStringBeginning(szPointer, "^SCTM_B: ", szPointer))
    {
        // The ^SCTM: URC anounces am upcomming emergency shoutdown or an warning.
        // This URC will show up on every multiplexer channel. To prevent to generate
        // multiple shutdown events, we will only look for the one on the URC channel.
        INT iParam;

        if (ParseTemperatureWarning(szPointer, iParam))
        {
            if (m_PortType == URC_PORT)
            {
                BOOL fShutdown;
                DWORD dwReason = EMERG_SHUTDOWN_NONE;

                if (iParam < 0)
                    dwReason = EMERG_SHUTDOWN_UNDERTEMPERATURE;
                else if (iParam > 0)
                    dwReason = EMERG_SHUTDOWN_OVERTEMPERATURE;

                // Reasons -2 and 2 indicate an immediate shutdown.
                fShutdown = ((iParam==-2 || iParam==2)?TRUE:FALSE);

                // AC45 specific: if an overtemeperature event occurs while an emergency call is
                // ongoing, the modem will not turn off.
                if (g_dwModemType == MODEMTYPE_SIEMENS_AC45 && g_fEmergencyCall)
                    fShutdown = FALSE;

                m_pRilHandle->BroadcastDWORDBlobNotification(fShutdown ? RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN : RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN_WARN, dwReason);

                if (fShutdown)
                    g_PowerManager.ForcePowerOff();
            }
        }
        m_fUnsolicited = TRUE;
        rcbNewLength = szPointer - m_szData; // Skip past the ^SCTM URC
        fRet = TRUE;
        goto Error;
    }
    else if (MatchStringAnywhere(szPointer, "+CME ERROR: ", szPointer))
    {
        // Extended error result
        UINT nCode;
        if (!ParseExtError(szPointer, g_rgemCMEErrors, NUM_CMEERRORS, nCode, FALSE))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Unrecognized CME Error response"));
            goto Error;
        }
        else
        {
            // An +CME ERROR: 132, 134, 261 or 263 within the USSD timeout period of an AT+CUSD
            // command is an unsolisticated result indicating, that the last USSD code is not
            // supported by the network.
            if ((nCode == 132 || nCode == 134 || nCode == 261 || nCode == 263 || nCode == 100) && m_PortType == COMMAND_PORT && g_dwLastCUSSDCmd > 0 && (GetTickCount() -g_dwLastCUSSDCmd) < USSD_TIMEOUT && !m_pRilHandle->FWaitingForRsp(m_PortType))
            {
#ifndef RIL_ADVANCEDUSSDERRORCAUSES
                // If we do not generate an advanced error cause, we have generate at least an 'USSD failed' message.
                RILSUPSERVICEDATA rssd;
                CNotificationData *pnd;

                rssd.dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                rssd.dwParams = RIL_PARAM_SSDI_STATUS;
                rssd.cbSize = sizeof(RILSUPSERVICEDATA);
                pnd = new CNotificationData;
                if (pnd)
                {
                    pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)&rssd,  sizeof(RILSUPSERVICEDATA));
                    m_pRilHandle->BroadcastNotification(pnd);
                }
#endif
                HandleUSSDError();

                g_dwLastCUSSDCmd = 0;  // Sometimes the module generates multiple error messages for one request, so we have to reset this value.
            }
            // The +CME ERROR: 17 (and 18) indicate, that the command was PIN2 related. We
            // have to set our PIN2 setting acordingly.
            else if (nCode == 17 || nCode == 18)
            {
                 g_bLastCmdPIN2 = TRUE;
            }
        }
#ifdef RIL_LAST_ERROR
        g_dwLastError = MAKELONG(nCode, RADIO_CMEERROR);
#endif
        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Got CME Error response"));
    }
    else if (MatchStringAnywhere(szPointer, "+CMS ERROR: ", szPointer))
    {
        // Extended error result
        UINT nCode;
        if (!ParseExtError(szPointer, g_rgemCMSErrors, NUM_CMSERRORS, nCode, FALSE))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Unrecognized CMS Error response"));
            goto Error;
        }
#ifdef RIL_LAST_ERROR
        g_dwLastError = MAKELONG(nCode, RADIO_CMSERROR);
#endif
        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Got CMS Error response"));
    }
    else
    {
        goto Error;
    }
    }

    if (!fExpectNothing)
    {
        if (fExpectCRLF)
        {
            if (!ParseRspPostfix(szPointer, szPointer))
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Failed to find <cr><lf> at end of response"));
                goto Error;
            }
        }
        else if (!MatchStringBeginning(szPointer, "\r", szPointer))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Failed to find <cr> at end of response"));
            goto Error;
        }
    }
    rcbNewLength = szPointer - m_szData;
    fRet = TRUE;

    if (!fSuppressLogging)
    {
        g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_PARSEDNOTIFICATION, PrintableString(m_szData, rcbNewLength));
    }

    Error:
    if (!fRet)
    {
        if (m_fPotentialBogusResponse)
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Attempting to filter bogus response"));

            // Heuristic used is to parse everything up to the first CR.
            if (MatchStringAnywhere(szPointer, "\r", szPointer))
            {
                // If the CR is followed by a LF, then consume that, too.
                (void)MatchStringBeginning(szPointer, "\n", szPointer);

                // If what was just parsed is followed by a simple v.25ter response,
                // then consume that as well.
                if (MatchStringBeginning(szPointer+1, "\r", szPointer))
                {
                    (void)MatchStringBeginning(szPointer, "\n", szPointer);
                }

                // The drawback with the preceding heuristic is that two
                // consecutive responses could be consumed, the second one
                // being potentially valid.

                // Whatever is parsed here should be treated as unrecognized
                m_fUnrecognized = TRUE;

                rcbNewLength = szPointer - m_szData;
                fRet = TRUE;

                if (!fSuppressLogging)
                {
                    g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_PARSEDGARBAGE, PrintableString(m_szData, rcbNewLength));
                }
            }
        }

        // Blow away a blob we may have allocated
        DeleteBlob();
    }

    m_fPotentialBogusResponse = FALSE;

    return fRet;
}


//
//
//
BOOL CResponse::ParseV25Response(const UINT nResponseCode)
{
    //TBD_FUNCTION(CResponse::ParseV25Response);
    RILRINGINFO rri; memset(&rri,0,sizeof(rri)); // zero struct
    RILCONNECTINFO rci; memset(&rci,0,sizeof(rci)); // zero struct
    BOOL fRet = FALSE;

    // NOTE: if IMSI can ever be shorter than 3 characters, this code won't help us
    if (!nResponseCode || 99 < nResponseCode)
    {
        // This can't be a response code
        goto Error;
    }

    switch (nResponseCode)
    {
    case 2:
        // In order not to generate multiple RING signals, we accept RING
        // from our URC port only.
        if (m_PortType == URC_PORT) {
            g_fEmergencyCall = FALSE;
            m_fUnsolicited = TRUE;
            m_dwCode = RIL_NOTIFY_RING;
            rri.cbSize = sizeof(RILRINGINFO);
            rri.dwCallType = RIL_CALLTYPE_UNKNOWN;
            rri.dwParams |= RIL_PARAM_RI_CALLTYPE;
            if (!SetBlob((void*)&rri, sizeof(RILRINGINFO)))
            {
                goto Error;
            }
        }
        break;

    case 3:
        g_fEmergencyCall = FALSE;
        m_fUnsolicited = FALSE;
        m_dwCode = RIL_RESULT_NOCARRIER;
        if ((m_PortType == URC_PORT &&  !IS_ADI) || (m_PortType == COMMAND_PORT && IS_ADI))
            CreateThread(NULL, 0, ACMMaxThreadProc, (LPVOID)m_pRilHandle, 0, 0);
        break;

    case 6:
        g_fEmergencyCall = FALSE;
        m_fUnsolicited = FALSE;
        m_dwCode = RIL_RESULT_NODIALTONE;
        if ((m_PortType == URC_PORT &&  !IS_ADI) || (m_PortType == COMMAND_PORT && IS_ADI))
            CreateThread(NULL, 0, ACMMaxThreadProc, (LPVOID)m_pRilHandle, 0, 0);
#ifdef RIL_ADVANCEDCALLRELEASEINDICATIONS
        if (m_PortType == COMMAND_PORT)
            CreateThread(NULL, 0, InvestigateCallReleaseThreadProc, (LPVOID)m_pRilHandle, 0, 0);
#endif
        break;

    case 7:
        g_fEmergencyCall = FALSE;
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_RESULT_BUSY;
        break;

    case 8:
        g_fEmergencyCall = FALSE;
        m_fUnsolicited = FALSE;
        m_dwCode = RIL_RESULT_NOANSWER;
        break;

    case 10:
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CONNECT;
        rci.dwBaudRate = 300;
        rci.dwParams |= RIL_PARAM_CNI_BAUDRATE;
        break;

    case 11:
    case 12:
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CONNECT;
        rci.dwBaudRate = 1200;
        rci.dwParams |= RIL_PARAM_CNI_BAUDRATE;
        break;

    case 13:
    case 47:
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CONNECT;
        rci.dwBaudRate = 2400;
        rci.dwParams |= RIL_PARAM_CNI_BAUDRATE;
        break;

    case 14:
    case 48:
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CONNECT;
        rci.dwBaudRate = 4800;
        rci.dwParams |= RIL_PARAM_CNI_BAUDRATE;
        break;

    case 15:
    case 49:
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CONNECT;
        rci.dwBaudRate = 9600;
        rci.dwParams |= RIL_PARAM_CNI_BAUDRATE;
        break;

    case 50:
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CONNECT;
        rci.dwBaudRate = 14400;
        rci.dwParams |= RIL_PARAM_CNI_BAUDRATE;
        break;

    case 0:
    case 4:
    case 9:
        goto Error;

    default:
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CONNECT;
        break;
    }

    // For data connections, set up the associated RILCONNECTINFO structure
    if (RIL_NOTIFY_CONNECT == m_dwCode)
    {
        rci.cbSize = sizeof(RILCONNECTINFO);
        rci.dwCallType = RIL_CALLTYPE_DATA;
        rci.dwParams |= RIL_PARAM_CNI_CALLTYPE;
        if (!SetBlob((void*)&rci, sizeof(RILCONNECTINFO)))
        {
            goto Error;
        }
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareErrors(const void* pElem1, const void* pElem2)
{
    DWORD dwCode1 = ((ERRORMAP*)pElem1)->dwCode;
    DWORD dwCode2 = ((ERRORMAP*)pElem2)->dwCode;
    int iRet;

    if (dwCode1 < dwCode2)
    {
        iRet = -1;
    }
    else if (dwCode1 == dwCode2)
    {
        iRet = 0;
    }
    else
    {
        iRet = 1;
    }
    return iRet;
}


//
//
//
BOOL CResponse::ParseExtError(LPCSTR& rszPointer, const ERRORMAP* const rgemMap, const UINT nMapEntries, UINT &nCode, BOOL fSigned)
{
    //TBD_FUNCTION(CResponse::ParseExtError);
    HRESULT hrError;
    ERRORMAP emKey;
    ERRORMAP* pemFound;
    LPCSTR szDummy;
    BOOL fRet = FALSE;

    // Look for a "<cr>" to make sure we got the whole number
    if (!MatchStringAnywhere(rszPointer, "\r", szDummy))
    {
        // This isn't a complete error notification -- no need to parse it
        goto Error;
    }

    if (fSigned)
        fRet = ParseIntAsUInt(rszPointer, nCode, rszPointer);
    else
        fRet = ParseUInt(rszPointer, FALSE, nCode, rszPointer);

    if (!fRet)
    {
        // We failed parsing
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }

    emKey.dwCode = nCode;
    pemFound = (ERRORMAP*)bsearch(&emKey, rgemMap, nMapEntries, sizeof(ERRORMAP), BSCompareErrors);
    hrError = (pemFound ? pemFound->hrError : E_FAIL);

    m_fUnsolicited = FALSE;
    m_dwCode = RIL_RESULT_ERROR;
    if (!SetBlob((void*)&hrError, sizeof(HRESULT)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::ParseExtRing(LPCSTR& rszPointer, DWORD *pdwCallType)
{
    //TBD_FUNCTION(CResponse::ParseExtRing);
    DWORD dwDefaultAddressId = 0;
    LPCSTR szDummy;
    RILRINGINFO rri; memset(&rri,0,sizeof(rri)); // zero struct
    BOOL fPostfixFound;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete ring notification -- no need to parse it
        goto Error;
    }

#ifdef TBD_DEBUG
    // For multi-line testing purposes, we can pretend that
    // an incoming call is on a line other than line 0.
    GetRegistryDWORD(HKEY_LOCAL_MACHINE,
                     g_tszRegKeyRIL,
                     TEXT("SpoofIncomingLine"),
                     &dwDefaultAddressId);
#endif

#ifdef RIL_ENABLE_ALS
    dwDefaultAddressId = g_dwALSLine;
    // The ^SALS and the +CRING URC show up within the same Mux frame, so the URC is already in the buffer.
    if (MatchStringAnywhere(rszPointer, "^SALS: 2", szDummy) )
        dwDefaultAddressId = 1;
#endif

    // Start by assuming that this ring is on the first line.
    rri.dwAddressId = dwDefaultAddressId;
    rri.dwParams |= RIL_PARAM_RI_ADDRESSID;

    if (MatchStringBeginning(rszPointer, "ASYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = FALSE;
        rri.rsiServiceInfo.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "SYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = TRUE;
        rri.rsiServiceInfo.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "REL ASYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = FALSE;
        rri.rsiServiceInfo.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "REL SYNC\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = TRUE;
        rri.rsiServiceInfo.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "FAX\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_FAX;
    }
    else if (MatchStringBeginning(rszPointer, "VOICE\r\n", rszPointer))
    {
        rri.dwCallType = RIL_CALLTYPE_VOICE;
    }
    else if (MatchStringBeginning(rszPointer, "AUX VOICE\r\n", rszPointer))
    {
        rri.dwAddressId = 1;        // AUX VOICE: voice call on 2nd line.
        rri.dwCallType = RIL_CALLTYPE_VOICE;
    }
    else
    {
        // Skip to the next <postfix>
        fPostfixFound = FindRspPostfix(rszPointer, rszPointer);
        TBD_ASSERT(FALSE != fPostfixFound);
        rri.dwCallType = RIL_CALLTYPE_UNKNOWN;
    }

    *pdwCallType = rri.dwCallType;
    
    // Walk back over the <CR><LF>
    rszPointer -= 2;

#ifdef TBD_DEBUG
    DWORD dwIgnoreIncomingCalls;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE,
                         g_tszRegKeyRIL,
                         TEXT("IgnoreIncomingCalls"),
                         &dwIgnoreIncomingCalls) &&
        0 != dwIgnoreIncomingCalls)
    {
        // We just successfully parsed a +CRING notification.
        // Ignore it for testing.
        return TRUE;
    }
#endif

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_RING;
    rri.cbSize = sizeof(RILRINGINFO);
    rri.dwParams |= RIL_PARAM_RI_CALLTYPE;
    if (RIL_CALLTYPE_DATA == rri.dwCallType)
    {
        // For data calls, set connection service info
        rri.dwParams |= RIL_PARAM_RI_SERVICEINFO;
        rri.rsiServiceInfo.cbSize = sizeof(RILSERVICEINFO);
        rri.rsiServiceInfo.dwParams |= RIL_PARAM_SVCI_ALL;
    }

    // We accept data and fax call only from the data port and all other calls only from the URC port.
    if (((rri.dwCallType == RIL_CALLTYPE_DATA || rri.dwCallType == RIL_CALLTYPE_FAX) && m_PortType == DATA_PORT) || m_PortType == URC_PORT)
    {
        if (!SetBlob((void*)&rri, sizeof(RILRINGINFO)))
        {
            goto Error;
        }
    }
    else
    {
        m_fUnsolicited = FALSE;
        m_dwCode = 0;
        if (!SetBlob(NULL, 0))
        {
            goto Error;
        }
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL  CResponse::ParseServiceInfo(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseServiceInfo);
    LPCSTR szDummy;
    RILSERVICEINFO rsi; memset(&rsi,0,sizeof(rsi)); // zero struct
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete ring notification -- no need to parse it
        goto Error;
    }

    if (MatchStringBeginning(rszPointer, "ASYNC", rszPointer))
    {
        rsi.fSynchronous = FALSE;
        rsi.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "SYNC", rszPointer))
    {
        rsi.fSynchronous = TRUE;
        rsi.fTransparent = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "REL ASYNC", rszPointer))
    {
        rsi.fSynchronous = FALSE;
        rsi.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "REL SYNC", rszPointer))
    {
        rsi.fSynchronous = TRUE;
        rsi.fTransparent = FALSE;
    }
    else if (MatchStringBeginning(rszPointer, "GPRS", rszPointer))
    {
        rsi.fSynchronous = FALSE;
        rsi.fTransparent = FALSE;
    }
    else
    {
        // "+CR: " must be followed by something valid
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }


    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_DATASVCNEGOTIATED;
    rsi.cbSize = sizeof(RILSERVICEINFO);
    rsi.dwParams |= RIL_PARAM_SVCI_ALL;
    if (!SetBlob((void*)&rsi, sizeof(RILSERVICEINFO)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::ParseRemotePartyInfo(LPCSTR& rszPointer, BOOL fCLIP)
{
    //TBD_FUNCTION(CResponse::ParseRemotePartyInfo);

    LPCSTR szDummy;
    UINT nValue;
    char szAddress[MAXLENGTH_ADDRESS];
    char szSubAddress[MAXLENGTH_SUBADDR];
    RILREMOTEPARTYINFO rrpi ; memset(&rrpi,0,sizeof(rrpi)); // zero struct
    BOOL fAddressPresent = FALSE;
    BOOL fSubAddressPresent = FALSE;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete Caller ID notification -- no need to parse it
        goto Error;
    }

    rrpi.cbSize = sizeof(RILREMOTEPARTYINFO);

    // Parse "<address>
    if (ParseString(rszPointer, szAddress, MAXLENGTH_ADDRESS, rszPointer))
    {
        fAddressPresent = TRUE;
    }

    // Parse ","
    if (!MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        goto Error;
    }

    // Parse "<type>"
    if (ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer) &&
        fAddressPresent                                                       &&
        StringToRILAddress(szAddress, (BYTE)nValue, rrpi.raAddress))
    {
        rrpi.dwParams |= RIL_PARAM_RPI_ADDRESS;
    }

    // Parse ","
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        // Parse "<subaddr>"
        if (ParseString(rszPointer, szSubAddress, MAXLENGTH_SUBADDR, rszPointer))
        {
            fSubAddressPresent = TRUE;
        }

        // Parse ","
        if (!MatchStringBeginning(rszPointer, ",", rszPointer))
        {
            goto Error;
        }

        // Parse "<subaddr_type>"
        if (ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer) &&
            fSubAddressPresent                                                    &&
            StringToRILSubAddress(szSubAddress, (BYTE)nValue, rrpi.rsaSubAddress))
        {
            rrpi.dwParams |= RIL_PARAM_RPI_SUBADDRESS;
        }

        // Parse ","
        if (MatchStringBeginning(rszPointer, ",", rszPointer))
        {
            // Parse "<description>"
            if (ParseQuotedEncodedString(   ENCODING_UCS2,
                                            rszPointer,
                                            rrpi.wszDescription,
                                            rrpi.wszDescription + MAXLENGTH_DESCRIPTION))
            {
                rrpi.dwParams |= RIL_PARAM_RPI_DESCRIPTION;
            }

            // Parse ","
            if (fCLIP && MatchStringBeginning(rszPointer, ",", rszPointer))
            {
                // Parse "<validity>"
                if (!ParseUIntAndVerifyAbove(rszPointer, FALSE, NUM_VALIDITIES, nValue, rszPointer))
                {
                    goto Error;
                }
                rrpi.dwValidity = g_rgdwValidities[nValue];
                rrpi.dwParams |= RIL_PARAM_RPI_VALIDITY;
            }
        }
    }

    if (!fCLIP && strlen(m_pRilHandle->GetLastDialedNumber())>0 )
    {
        if (wcslen(rrpi.raAddress.wszAddress)==0)
            wsprintf(rrpi.raAddress.wszAddress, L"%S", m_pRilHandle->GetLastDialedNumber());
        
        m_pRilHandle->DeleteLastDialedNumber();
    }

#ifdef TBD_DEBUG
    DWORD dwIgnoreIncomingCalls;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE,
                         g_tszRegKeyRIL,
                         TEXT("IgnoreIncomingCalls"),
                         &dwIgnoreIncomingCalls) &&
        0 != dwIgnoreIncomingCalls)
    {
        // We just successfully parsed a +CLIP notification.
        // Ignore it for testing.
        return TRUE;
    }
#endif

    m_fUnsolicited = TRUE;
    m_dwCode = (fCLIP ? RIL_NOTIFY_CALLERID : RIL_NOTIFY_DIALEDID);
    if (!SetBlob((void*)&rrpi, sizeof(RILREMOTEPARTYINFO)))
    {
        goto Error;
    }
#ifdef RIL_INDICATE_COLP
    if (!fCLIP)
    {
        WCHAR wszText[MAX_RESOURCE_STRING_LEN];
        CNotificationData* pnd = NULL;
        RILSUPSERVICEDATA *prssd;
        INT cbStruct;

        if (LoadString((HINSTANCE)hInstance, IDS_INDICATION_COLP, wszText, MAX_RESOURCE_STRING_LEN) > 0)
        {
            WCHAR wszOut[MAX_RESOURCE_STRING_LEN];

            wsprintf(wszOut, wszText, szAddress);
            cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszOut) * sizeof(WCHAR)) + sizeof(WCHAR);
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
            memcpy(prssd->pbData, (BYTE *)wszOut, (wcslen(wszOut) * sizeof(WCHAR)) + sizeof(WCHAR));
            prssd->dwParams = RIL_PARAM_SSDI_DATA;
            prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
            prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
            prssd->cbSize = cbStruct;
            pnd = new CNotificationData;
            if (pnd)
            {
                pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize);
                m_pRilHandle->BroadcastNotification(pnd);
            }
        }
    }
#endif
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CResponse::ParseCallWaitingInfo(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseCallWaitingInfo);

    LPCSTR szDummy;
    UINT nValue;
    BOOL fAddressPresent = FALSE;
    char szAddress[MAXLENGTH_ADDRESS];
    RILCALLWAITINGINFO rcwi ; memset(&rcwi,0,sizeof(rcwi)); // zero struct
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete Caller ID notification -- no need to parse it
        goto Error;
    }

    rcwi.cbSize = sizeof(RILCALLWAITINGINFO);

    // Parse "<address>
    if (ParseString(rszPointer, szAddress, MAXLENGTH_ADDRESS, rszPointer))
    {
        fAddressPresent = TRUE;
    }

    // Parse ","
    if (!MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        goto Error;
    }

    // Parse "<type>"
    if (ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer) &&
        fAddressPresent                                                       &&
        StringToRILAddress(szAddress, (BYTE)nValue, rcwi.rrpiCallerInfo.raAddress))
    {
        rcwi.dwParams |= RIL_PARAM_CWI_CALLERINFO;
        rcwi.rrpiCallerInfo.dwParams |= RIL_PARAM_RPI_ADDRESS;
    }

    // Parse ",<class>"
    if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
        !ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }
    if (1 == nValue)
    {
        rcwi.dwCallType = RIL_CALLTYPE_VOICE;
    }
    else if (2 == nValue || 16 == nValue || 32 == nValue || 64 == nValue || 128 == nValue)
    {
        rcwi.dwCallType = RIL_CALLTYPE_DATA;
    }
    else if (4 == nValue)
    {
        rcwi.dwCallType = RIL_CALLTYPE_FAX;
    }
    else
    {
        rcwi.dwCallType = RIL_CALLTYPE_UNKNOWN;
    }
    rcwi.dwParams |= RIL_PARAM_CWI_CALLTYPE;

    // Parse ","
    if (MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        if (IS_ADI)
        {
            // Only the MC75 module supports the <description> parameter, but this may change.
            // Parse "<description>"
            if (ParseQuotedEncodedString(ENCODING_TECHARSET,
                                          rszPointer,
                                          rcwi.rrpiCallerInfo.wszDescription,
                                          rcwi.rrpiCallerInfo.wszDescription + MAXLENGTH_DESCRIPTION))
            {
                rcwi.dwParams |= RIL_PARAM_CWI_CALLERINFO;
                rcwi.rrpiCallerInfo.dwParams |= RIL_PARAM_RPI_DESCRIPTION;
            }
        }
        // Parse ","
        if (MatchStringBeginning(rszPointer, ",", rszPointer))
        {
            // Parse "<validity>"
            if (!ParseUIntAndVerifyAbove(rszPointer, FALSE, NUM_VALIDITIES, nValue, rszPointer))
            {
                goto Error;
            }
            rcwi.rrpiCallerInfo.dwValidity = g_rgdwValidities[nValue];
            rcwi.dwParams |= RIL_PARAM_CWI_CALLERINFO;
            rcwi.rrpiCallerInfo.dwParams |= RIL_PARAM_RPI_VALIDITY;
        }
    }

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_CALLWAITING;
    if (!SetBlob((void*)&rcwi, sizeof(RILCALLWAITINGINFO)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

#ifdef SIEMENS_MAGNETO
//
//
//
BOOL CResponse::ParseUnsolicitedSSInfo(LPCSTR& szPointer)
{
// Parsing for the +CCSU notification. The format is:
//    "<code2>[,<index>[,<address>,<type>[,<subaddress>,<satype>]]]"

    //TBD_FUNCTION(CResponse::ParseUnsolicitedSSInfo);

    LPCSTR szDummy;
    UINT nDummy;
    DWORD dwTemp;
    LPSTR szPostfix;
    BOOL fRet = FALSE;
#ifdef RIL_INDICATE_SS_INFORMATION
    WCHAR wszText[MAX_RESOURCE_STRING_LEN];
    UINT uiText = 0;
    UINT i;
#endif

    if (!FindRspPostfix(szPointer, szPostfix))
    {
        // This isn't a complete Supplementary services notification -- no need to parse it
        goto Error;
    }

    // parse <code2>
    if (!ParseUIntAndVerifyAbove(szPointer, FALSE, RIL_UNSSSCODE_MAX+1, nDummy, szPointer))
    {
        goto Error;
    }
    if(5 == nDummy)
    {
        // A notification indicating that a call on hold has been remotely hung up
        // Find "<postfix>"
        if (!ParseRspPostfix(szPointer, szDummy))
        {
            goto Error;
        }
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_DISCONNECT;
        dwTemp = RIL_DISCINIT_REMOTE;
        if (!SetBlob((void*)&dwTemp, sizeof(DWORD)))
        {
            goto Error;
        }
        else
        {
            fRet = TRUE;
        }
    }
    else
    {
        // A "real" SS Notification
        RILUNSOLICITEDSSINFO russi;
        BOOL fHaveAddress = FALSE;
        char szAddress[MAXLENGTH_ADDRESS];
        char szSubAddress[MAXLENGTH_ADDRESS];

        russi.cbSize = SIZEOF(RILUNSOLICITEDSSINFO);
        russi.dwNotificationCode = nDummy;
        russi.dwParams = RIL_PARAM_UNSSS_NOTIFICATIONCODE;

        // parse [,<index>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Continue;
        }
        if (ParseUInt(szPointer, TRUE, nDummy, szPointer))
        {
            russi.dwCallUserGroupIndex = nDummy;
            russi.dwParams |= RIL_PARAM_UNSSS_CUGINDEX;
        }

        // parse [,<number>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Continue;
        }
        if(ParseString(szPointer, szAddress, MAXLENGTH_ADDRESS, szPointer))
        {
             fHaveAddress = TRUE;
        }
        // Parse "," and look for <type>
        if (MatchStringBeginning(szPointer, ",", szPointer))
        {
            UINT nType = 0;
            if(ParseUInt(szPointer, FALSE, nType, szPointer))
            {
                if(( nType < 0x100) && fHaveAddress)
                {
                    if(StringToRILAddress(szAddress, (BYTE)nType, russi.raAddress))
                    {
                        russi.dwParams |= RIL_PARAM_UNSSS_ADDRESS;
                        RILRetailTrace((TEXT("Parse CSSU: found address and type \r\n")));
                    }
                    else
                    {
                        goto Error;
                    }
                }
            }
        }

        // parse [,<subaddr>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Continue;
        }
        fHaveAddress = FALSE;
        if(ParseString(szPointer, szAddress, MAXLENGTH_ADDRESS, szPointer))
        {
             fHaveAddress = TRUE;
        }
        // Parse "," and look for <satype>
        if (MatchStringBeginning(szPointer, ",", szPointer))
        {
            UINT nType = 0;
            if(ParseUInt(szPointer, FALSE, nType, szPointer))
            {
                if(( nType < 0x100) && fHaveAddress)
                {
                    if(StringToRILSubAddress(szSubAddress, (BYTE)nType, russi.rsaSubAddress))
                    {
                        russi.dwParams |= RIL_PARAM_UNSSS_SUBADDR;
                        RILRetailTrace((TEXT("Parse CSSU: found subaddress and type \r\n")));
                    }
                    else
                    {
                        goto Error;
                    }
                }
            }
        }

Continue:
        // Find "<postfix>"
        if (!ParseRspPostfix(szPointer, szDummy))
        {
            goto Error;
        }
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_UNSOLICITEDSS;
        if (!SetBlob((void*)&russi, sizeof(RILUNSOLICITEDSSINFO)))
        {
            goto Error;
        }
        else
        {
            fRet = TRUE;
        }
    }

#ifdef RIL_INDICATE_SS_INFORMATION
    for (i = 0; i < NUM_CSSUINDICATIONSCAUSES; i++)
    {
        if (g_rgCSSUIndicationsTable[i].dwSS_ind == nDummy)
        {
            uiText = g_rgCSSUIndicationsTable[i].uiErrorText;
            break;
        }
    }

    if (uiText != 0)
    {
        CNotificationData* pnd = NULL;
        RILSUPSERVICEDATA *prssd;
        INT cbStruct;

        if (LoadString((HINSTANCE)hInstance, uiText, wszText, MAX_RESOURCE_STRING_LEN) > 0)
        {
            cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);

            memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
            prssd->dwParams = RIL_PARAM_SSDI_DATA;
            prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
            prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
            prssd->cbSize = cbStruct;
            pnd = new CNotificationData;
            if (pnd)
            {
                pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize);
                m_pRilHandle->BroadcastNotification(pnd);
            }
        }
    }
#endif

Error:

    return fRet;
}




//
//
//
BOOL CResponse::ParseIntermediateSSInfo(LPCSTR& szPointer)
{
    //TBD_FUNCTION(CResponse::ParseIntermediateSSInfo);
// Parsing for the +CCSI notification. The format is:
//    "<code1>[,<index>]"
// <index> is only present when <code1> is 4 (CUG call)

    LPCSTR szDummy;
    UINT nDummy;
    LPSTR szPostfix;
    BOOL fRet = FALSE;
    RILINTERMEDIATESSINFO rissi;
#ifdef RIL_INDICATE_SS_INFORMATION
    WCHAR wszText[MAX_RESOURCE_STRING_LEN];
    UINT uiText = NULL;
    UINT i;
#endif

    if (!FindRspPostfix(szPointer, szPostfix))
    {
        // This isn't a complete Supplementary services notification -- no need to parse it
        goto Error;
    }

    // parse <code1>
    if (!ParseUIntAndVerifyAbove(szPointer, FALSE, RIL_INTSSCODE_MAX+1, nDummy, szPointer))
    {
        goto Error;
    }

    rissi.cbSize = SIZEOF(RILINTERMEDIATESSINFO);
    rissi.dwNotificationCode = nDummy;
    rissi.dwParams = RIL_PARAM_INTSS_NOTIFICATIONCODE;

    if (4 == nDummy)
    {
        // parse ,<index>
        if (!MatchStringBeginning(szPointer, ",", szPointer))
        {
            goto Error;
        }
        if (ParseUInt(szPointer, TRUE, nDummy, szPointer))
        {
            rissi.dwCallUserGroupIndex = nDummy;
            rissi.dwParams |= RIL_PARAM_INTSS_CUGINDEX;
        }
        else
        {
            goto Error;
        }
    }

    // Find "<postfix>"
    if (!ParseRspPostfix(szPointer, szDummy))
    {
        goto Error;
    }
    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_INTERMEDIATESS;
    if (!SetBlob((void*)&rissi, sizeof(RILINTERMEDIATESSINFO)))
    {
        goto Error;
    }
    else
    {
        fRet = TRUE;
    }

#ifdef RIL_INDICATE_SS_INFORMATION
    for (i = 0; i < NUM_CSSIINDICATIONSCAUSES; i++)
    {
        if (g_rgCSSIIndicationsTable[i].dwSS_ind == nDummy)
        {
            uiText = g_rgCSSIIndicationsTable[i].uiErrorText;
            break;
        }
    }

    if (uiText != 0)
    {
        CNotificationData* pnd = NULL;
        RILSUPSERVICEDATA *prssd;
        INT cbStruct;

        if (LoadString((HINSTANCE)hInstance, uiText, wszText, MAX_RESOURCE_STRING_LEN) > 0)
        {
            cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);

            memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
            prssd->dwParams = RIL_PARAM_SSDI_DATA;
            prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
            prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
            prssd->cbSize = cbStruct;
            pnd = new CNotificationData;
            if (pnd)
            {
                pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize);
                m_pRilHandle->BroadcastNotification(pnd);
            }
        }
    }
#endif

Error:
    return fRet;
}
#endif


//
//
//
BOOL CResponse::ParseCallMeter(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseCallMeter);
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete CCM notification -- no need to parse it
        goto Error;
    }

    // Parse "\"<ccm>\""
    if (!MatchStringBeginning(rszPointer, "\"", rszPointer)  ||
        !ParseHexUInt(rszPointer, TRUE, nValue, rszPointer) ||
        !MatchStringBeginning(rszPointer, "\"", rszPointer))
    {
        // Parsing failure
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_CALLMETER;
    if (!SetBlob((void*)&nValue, sizeof(DWORD)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


extern DWORD g_dwEnableGPRSAttach;

DWORD WINAPI GPRSReAttachThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;
    Sleep(5000);
    QueueInternalCmd(DATA_PORT, pHandle, "AT+CGATT=1\r", CMDOPT_NONE, APIID_SETINITIALGPRSATTACHED, GPRS_WAIT_FOR_ATTACH*100, 5, GPRS_WAIT_FOR_ATTACH*100);
    return 0;
}

#ifndef RIL_NO_CREG_LOCATION_UPDATES
//
// Sometimes the location information is missing in an +CREG URC. In this case we read the value manually.
//
DWORD WINAPI CheckLocationUpdateThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;
    Sleep(1500);
    // To be on the safe side, we not only read the current value, we re-activate the URC itself.
    QueueInternalCmd(URC_PORT, pHandle, "AT+CREG=2;+CREG?\r", CMDOPT_NONE, APIID_GETREGISTRATIONSTATUS, 0, 0, 0, ParseGetRegistrationStatus, NULL, pVoid);
    return 0;
}
#endif

//
// This thread is called, when a call is established on an MC75 Release 2
//
DWORD WINAPI AudioParamThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle;
    DWORD dwDevice;

    pHandle = (CRilHandle *)pVoid;

    TBD_FUNCTION(AudioParamThreadProc);

    Sleep(250);
    dwDevice = pHandle->GetAudioRxDevice();
    // If the current audio mode is on a digital interface, we hafe to switch to analog and then to digital
    // to re-initialize the audio interface.
    if (g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwAudioMode > 1 && g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwInterface == 1)
    {
        char szCmd[MAX_ATCMD_LEN];

       // Set to analog.
       sprintf(szCmd, "AT^SNFS=%d;^SAIC=%d,%d,%d\r", g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwAudioMode, 2, g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwMicrophone, g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwEarpiece);
       QueueInternalCmd(URC_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, 0, 0, 0, NULL, NULL, NULL);
       // Wait a moment and then back to digital.
       Sleep(50);
       sprintf(szCmd, "AT^SNFS=%d;^SAIC=%d,%d,%d\r", g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwAudioMode, 1, g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwMicrophone, g_acAudioConfig[GetAudioTableIndex(dwDevice)].dwEarpiece);
       QueueInternalCmd(URC_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, 0, 0, 0, NULL, NULL, NULL);
    }

    return 0;
}

#ifdef RIL_USE_CSQ_NOTIFICATIONS
//
// In this thread we send a notification about a change of the signal strength, also the radio
// hasn't reported one. This is necessary after changes of the registration change, since
// the MMI expects a new notification of the signal strength, even when the old one is still
// valid.
//
DWORD WINAPI ReSendSignalThreadProc(LPVOID pVoid)
{
    CNotificationData* pnd = NULL;
    RILSIGNALQUALITY rsq;
    CRilHandle* pHandle;

    pHandle = (CRilHandle *)pVoid;

    Sleep(750);
    FillRSQStruct(&rsq);
    pnd = new CNotificationData;
    if (pnd)
    {
        pnd->InitFromRealBlob(RIL_NOTIFY_SIGNALQUALITY, (void*)&rsq, rsq.cbSize);
        pHandle->BroadcastNotification(pnd);
    }
    return 0;
}
#endif

#if defined(RIL_SYSTEMTYPE_EDGE)
static HRESULT ParseGPRSMonitor(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    //TBD_FUNCTION(ParseGetExtendedErrorReport);
    UINT nValue;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (!MatchStringAnywhere(szRsp, "\r\n", szRsp) || !MatchStringAnywhere(szRsp, "\r\n", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    nValue = (UINT)atoi(szRsp + 6);

    // Find "<postfix>"
    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    g_dwEDGEStatus = nValue;

Error:
    return hr;
}

DWORD WINAPI CheckEDGEStatusThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    // MC75 specific: with the AT^SMONG command we are able to tell if we are attached to GPRS or EDGE.
    // Under Magneto we are able to notify the OS about this.
    g_dwEDGEStatus = 0;
    if (QueueInternalCmdBlocking(URC_PORT, pHandle, "AT^SMONG\r", CMDOPT_NONE, APIID_NONE, SMONG_TIMEOUT, 0, 0, ParseGPRSMonitor))
    {
        switch(g_dwEDGEStatus) {
        case 1:         // 1 or 2: GPRS
        case 2:
            if (g_dwLastEDGEStatus != RIL_SYSTEMTYPE_GPRS)
                pHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCHANGED, RIL_SYSTEMTYPE_GSM | RIL_SYSTEMTYPE_GPRS);
            g_dwLastEDGEStatus = RIL_SYSTEMTYPE_GPRS;
            break;
        case 3:         // 3 or 4: EDGE
        case 4:
            if (g_dwLastEDGEStatus != RIL_SYSTEMTYPE_EDGE)
                pHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_SYSTEMCHANGED, RIL_SYSTEMTYPE_GSM | RIL_SYSTEMTYPE_GPRS | RIL_SYSTEMTYPE_EDGE);
            g_dwLastEDGEStatus = RIL_SYSTEMTYPE_EDGE;
            break;
        }
    }

    return 0;
}
#endif

//
//
//
static HRESULT ParseGetExtendedErrorReport(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    //TBD_FUNCTION(ParseGetExtendedErrorReport);
    UINT nValue1, nValue2, nValue3;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+CEER: <locationID>, <reason>, <ssRelease><cr><lf>"
    if(!MatchStringBeginning(szRsp, "+CEER: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue1, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue2, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue3, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Find "<postfix>"
    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    g_dwCEER_LocID = nValue1;
    g_dwCEER_reason = nValue2;
    g_dwCEER_ssRel = nValue3;

Error:
    return hr;
}

DWORD WINAPI ACMMaxThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    // The MC75 doesn't show the extended error code immediately after the call, so we have to wait.
    if (IS_ADI)
        Sleep(1500);

     // A call has ben ended and we now wand to find out about the reason. If the reason is,
    // that we reached the AC limit, we have to send the RIL_NOTIFY_CALLMETERMAXREACHED
    // notification.
    if (QueueInternalCmdBlocking((IS_ADI) ? COMMAND_PORT : URC_PORT, pHandle, "AT+CEER\r", CMDOPT_NONE, APIID_NONE, CEER_TIMEOUT, 0, 0, ParseGetExtendedErrorReport))
    {
        if (g_dwCEER_LocID == 8 && g_dwCEER_reason == 68)  // we assume: g_dwCEER_ssRel == 0
        {
            CNotificationData* pnd = new CNotificationData;

            if (pnd)
            {
#ifdef RIL_INDICATE_AOCMAX
                INT cbStruct;
                RILSUPSERVICEDATA *prssd;
                WCHAR wszText[MAX_RESOURCE_STRING_LEN];
                CNotificationData* pnd2 = new CNotificationData;
#endif

                pnd->InitFromRealBlob(RIL_NOTIFY_CALLMETERMAXREACHED, NULL, 0);
                pHandle->BroadcastNotification(pnd);

#ifdef RIL_INDICATE_AOCMAX
                if (LoadString((HINSTANCE)hInstance, IDS_AOC_INDICATION_AOCMAX, wszText, MAX_RESOURCE_STRING_LEN) > 0)
                {
                    cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
                    prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);

                    memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
                    prssd->dwParams = RIL_PARAM_SSDI_DATA;
                    prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
                    prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
                    prssd->cbSize = cbStruct;
                    pnd2 = new CNotificationData;
                    if (pnd2)
                    {
                        pnd2->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize);
                        pHandle->BroadcastNotification(pnd2);
                    }
                }
#endif
            }
        }
    }

    return 0;
}

#ifdef RIL_ADVANCEDUSSDERRORCAUSES
//
// This function generates error messages according to the current AT+CEER values and sends them
// to the MMI for display as faked USSD messages.
//
DWORD WINAPI GenerateMMIError(CRilHandle* pHandle, const USSDERRORCAUSE *prgErrorCausesTable, UINT uiNumErrorCauses, UINT uiDefaultMessage)
{
    // An USSD request failed, we now have do find out about the reason by query the AT+CEER values.
    Sleep(500);
    if (QueueInternalCmdBlocking(COMMAND_PORT, pHandle, "AT+CEER\r", CMDOPT_NONE, APIID_NONE, CEER_TIMEOUT, 0, 0, ParseGetExtendedErrorReport))
    {
        CNotificationData* pnd = NULL;
        RILSUPSERVICEDATA *prssd;
        INT cbStruct;
        UINT uiText = uiDefaultMessage;
        WCHAR wszText[MAX_RESOURCE_STRING_LEN];
        UINT i;

        for (i = 0; i < uiNumErrorCauses; i++)
        {
            if (prgErrorCausesTable[i].dwCEER_LocID==g_dwCEER_LocID && prgErrorCausesTable[i].dwCEER_reason==g_dwCEER_reason && prgErrorCausesTable[i].dwCEER_ssRel==g_dwCEER_ssRel)
            {
                uiText = prgErrorCausesTable[i].uiErrorText;
                break;
            }
        }

        if (LoadString((HINSTANCE)hInstance, uiText, wszText, MAX_RESOURCE_STRING_LEN) > 0)
        {
            cbStruct = sizeof(RILSUPSERVICEDATA) + (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR);
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);

            memcpy(prssd->pbData, (BYTE *)wszText, (wcslen(wszText) * sizeof(WCHAR)) + sizeof(WCHAR));
            prssd->dwParams = RIL_PARAM_SSDI_DATA;
            prssd->dwStatus = RIL_SUPSVCDATASTATUS_UNSUPPORTED;
            prssd->dwParams |= RIL_PARAM_SSDI_STATUS;
            prssd->cbSize = cbStruct;
            pnd = new CNotificationData;
            if (pnd)
            {
                pnd->InitFromRealBlob(RIL_NOTIFY_SUPSERVICEDATA, (void*)prssd, prssd->cbSize);
                pHandle->BroadcastNotification(pnd);
            }
        }
    }

    return 0;
}

DWORD WINAPI InvestigateUSSDThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    return GenerateMMIError(pHandle, g_rgUSSDErrorCausesTable, NUM_USSDERRORCAUSES, IDS_USSD_ERROR_UNKNOWN_REASON);
}
#endif

#ifdef RIL_ADVANCEDCALLRELEASEINDICATIONS
DWORD WINAPI InvestigateCallReleaseThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    return GenerateMMIError(pHandle, g_rgCallReleaseErrorCausesTable, NUM_CALLRELEASEERRORCAUSES, 0);
}
#endif

DWORD WINAPI CancelUSSDThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    // First let the InvestigateUSSD-Thread start working.
    Sleep(1500);

    // MC75 specific: if an USSD request failed (for example it has been an invalid code), we
    // have to cancel the USSD session manually.
    QueueInternalCmdBlocking(COMMAND_PORT, pHandle, "AT+CUSD=2\r", CMDOPT_NONE, APIID_NONE, CUSD_CANCEL_TIMEOUT, 0, 0, NULL);

    return 0;
}

//
//
//
BOOL CResponse::ParseCallListNotification(LPCSTR& rszPointer)
{
    char szAddress[MAXLENGTH_ADDRESS];
    LPCSTR szDummy;
    UINT nValue;
    BOOL bContinue = FALSE;
    BOOL bIncrement = FALSE;
    BOOL bRet = TRUE;
#ifdef RIL_ENABLE_ALS
    BOOL bNoRING = TRUE;
#endif

    if (!MatchStringAnywhere(rszPointer, "^SLCC: \r\n", szDummy) && !MatchStringBeginning(rszPointer, "\r\n", szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        bRet = FALSE;
        goto Error;
    }

    while (TRUE) {
        // Parse "<id>"
        if (!ParseUInt(rszPointer, TRUE, nValue, rszPointer)) {
            EnterCriticalSection(&g_CallInfoSection);
            g_iNrCallInfo = m_iNrCallInfo;
            memcpy(g_CallInfo, m_CallInfo, MAX_NUMBERCALLS * sizeof(RILCALLINFO));
            LeaveCriticalSection(&g_CallInfoSection);
            m_iNrCallInfo = 0;
            goto Error;
        }

        memset(&m_CallInfo[m_iNrCallInfo], 0x00, sizeof(RILCALLINFO));
        m_CallInfo[m_iNrCallInfo].cbSize = sizeof(RILCALLINFO);
        m_CallInfo[m_iNrCallInfo].raAddress.cbSize = sizeof(RILADDRESS);

        m_CallInfo[m_iNrCallInfo].dwID = nValue;
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_ID;

        // Parse ",<direction>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, 2, nValue, rszPointer)) {
            goto NextURC;
        }
        m_CallInfo[m_iNrCallInfo].dwDirection = (nValue ? RIL_CALLDIR_INCOMING : RIL_CALLDIR_OUTGOING);
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_DIRECTION;

        // Parse ",<status>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, NUM_CALLSTATS, nValue, rszPointer)) {
            goto NextURC;
        }
        m_CallInfo[m_iNrCallInfo].dwStatus = g_rgdwCallStats[nValue];
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_STATUS;

        // Parse ",<type>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, NUM_CALLTYPES, nValue, rszPointer)) {
            goto NextURC;
;
        }
        m_CallInfo[m_iNrCallInfo].dwType = g_rgdwCallTypes[nValue];
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_TYPE;

        // Parse ",<multiparty>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, 2, nValue, rszPointer)) {
            goto NextURC;
        }
        m_CallInfo[m_iNrCallInfo].dwMultiparty = (nValue ? RIL_CALL_MULTIPARTY : RIL_CALL_SINGLEPARTY);
        m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_TYPE;

        // Parse ",<tca>"
        if (!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUIntAndVerifyAbove(rszPointer, TRUE, 2, nValue, rszPointer)) {
            goto NextURC;
        }

#ifdef RIL_ENABLE_ALS
        if (m_CallInfo[m_iNrCallInfo].dwStatus == RIL_CALLSTAT_ALERTING)
            bNoRING = FALSE;
#endif

        bIncrement = TRUE;
        // Parse ","
        if (MatchStringBeginning(rszPointer, ",", rszPointer)) {
            // Parse "<address>,<type>"
            if (ParseString(rszPointer, szAddress, MAXLENGTH_ADDRESS, rszPointer)) {
                if(!MatchStringBeginning(rszPointer, ",", rszPointer))
                    bContinue = TRUE;

                if(!ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x1000, nValue, rszPointer))
                    bContinue = TRUE;

                nValue = (nValue > 0x100) ? nValue/10 : nValue;

                if(!StringToRILAddress(szAddress, (BYTE)nValue, m_CallInfo[m_iNrCallInfo].raAddress))
                    bContinue = TRUE;

                if(bContinue)
                    goto NextURC;
                m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_ADDRESS;
            }
            else {
                // If we couldn't parse an address, then it might be empty,
                // meaning the ID is blocked. Since the address parameter
                // is present, make sure the type also exists before continuing.
                if (!MatchStringBeginning(rszPointer, ",", rszPointer)                 ||
                    !ParseUIntAndVerifyAbove(rszPointer, FALSE, 0x100, nValue, rszPointer)) {
                    goto NextURC;
                }
            }

            // Parse ","
            if (MatchStringBeginning(rszPointer, ",", rszPointer)) {
            // Parse "<description>"
                if (!ParseQuotedEncodedString(ENCODING_UCS2, rszPointer, m_CallInfo[m_iNrCallInfo].wszDescription, m_CallInfo[m_iNrCallInfo].wszDescription + MAXLENGTH_DESCRIPTION)) {
                    goto NextURC;
                }
                m_CallInfo[m_iNrCallInfo].dwParams |= RIL_PARAM_CI_DESCRIPTION;
            }
        }
NextURC:
        if (bIncrement && m_iNrCallInfo < MAX_NUMBERCALLS-1)
            m_iNrCallInfo++;
        if (!MatchStringBeginning(rszPointer, "\r\n^SLCC: ", rszPointer))
            break;
    }
Error:
    if (bRet)
        g_LastATD = 0;      // needed for RILDrv_GetCallList(), see comments there.

#ifdef RIL_ENABLE_ALS
    if (bNoRING)
        g_dwALSLine = 0;
#endif

    // Workaround: the TAPI misinterprets the call states of CSD calls. So we
    // simply do not report this fact to the OS.
    if (bRet && !(g_iNrCallInfo==1 && (g_CallInfo[0].dwType==RIL_CALLTYPE_DATA || g_CallInfo[0].dwType==RIL_CALLTYPE_FAX))) {
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_CALLSTATECHANGED;
        SetBlob(NULL, 0);
        //RHADeactivateVoicePath();
    }
    // Special case: since the +CRING-URC is only activated on the aux-port, but incomming CSD calls are
    // signalized on the data port only, we have to emulate the +CRING: REL ASYNC URC here.
    if (bRet && (g_iNrCallInfo==1 && g_CallInfo[0].dwType==RIL_CALLTYPE_DATA && g_CallInfo[0].dwStatus==RIL_CALLSTAT_INCOMING)) {
        RILRINGINFO rri; memset(&rri,0,sizeof(rri)); // zero struct

        rri.dwAddressId = 0;
        rri.dwParams |= RIL_PARAM_RI_ADDRESSID;
        rri.dwCallType = RIL_CALLTYPE_DATA;
        rri.rsiServiceInfo.fSynchronous = TRUE;
        rri.rsiServiceInfo.fTransparent = FALSE;
        rri.cbSize = sizeof(RILRINGINFO);
        rri.dwParams |= RIL_PARAM_RI_CALLTYPE;
        // For data calls, set connection service info
        rri.dwParams |= RIL_PARAM_RI_SERVICEINFO;
        rri.rsiServiceInfo.cbSize = sizeof(RILSERVICEINFO);
        rri.rsiServiceInfo.dwParams |= RIL_PARAM_SVCI_ALL;
        SetBlob((void*)&rri, sizeof(RILRINGINFO));
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_RING;
    }
    return bRet;
}

//
//
//
BOOL CResponse::ParseIndicatorEventValue(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseIndicatorEventValue);
    LPCSTR szDummy;
    DWORD dwRegStatus;
    BOOL fRet = FALSE;
    UINT nValue;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "eons,"
    if (MatchStringBeginning(rszPointer, "eons,", rszPointer))
    {
        // We have an eons notification -> assume that the opeartor name has changed
        // and create a RIL_NOTIFY_REGSTATUSCHANGED in any case
        dwRegStatus = g_dwRegStatus;
        m_fUnsolicited = TRUE;
#ifdef RIL_NOTIFY_EONS
        m_dwCode = RIL_NOTIFY_EONS;
#else // RIL_NOTIFY_EONS
        m_dwCode = RIL_NOTIFY_REGSTATUSCHANGED;
        if (!SetBlob((void*)&dwRegStatus, sizeof(DWORD)))
        {
            goto Error;
        }
#endif // RIL_NOTIFY_EONS
        fRet = FindRspPostfix(rszPointer, rszPointer);
        if (fRet) {
          // Step back over found "<CR><LF>" which is parsed in ParseNotification() again
          rszPointer = rszPointer - 2;
        }
    }
    else if (MatchStringBeginning(rszPointer, "signal,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
#ifdef RIL_USE_CSQ_NOTIFICATIONS
            RILSIGNALQUALITY rsq;
#endif

            g_bCINDIsActive = TRUE;
            g_iLastSignal = nValue;

#ifdef RIL_USE_CSQ_NOTIFICATIONS
            FillRSQStruct(&rsq);

            m_fUnsolicited = TRUE;
            m_dwCode = RIL_NOTIFY_SIGNALQUALITY;
            if (!SetBlob((void*)&rsq, sizeof(RILSIGNALQUALITY)))
            {
                m_fUnsolicited = FALSE;
            }
#endif
       }
       fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "rssi,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
#ifdef RIL_USE_CSQ_NOTIFICATIONS
            RILSIGNALQUALITY rsq;
#endif

            g_bCINDIsActive = TRUE;
            g_iLastRSSI = nValue;

#ifdef RIL_USE_CSQ_NOTIFICATIONS
            FillRSQStruct(&rsq);

            m_fUnsolicited = TRUE;
            m_dwCode = RIL_NOTIFY_SIGNALQUALITY;
            if (!SetBlob((void*)&rsq, sizeof(RILSIGNALQUALITY)))
            {
                m_fUnsolicited = FALSE;
            }
#endif
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "call,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            // MC75 Rel2: after establishing the voice call a check of the audio parameters is necessary
            if (nValue == 1 && g_dwModemType == MODEMTYPE_SIEMENS_MC75 && g_dwModemVersion >= 1900)
                CreateThread(NULL, 0, AudioParamThreadProc, (LPVOID)m_pRilHandle, 0, 0);

            if (g_iNrCallInfo == -1) {
                m_fUnsolicited = TRUE;
                m_dwCode = RIL_NOTIFY_CALLSTATECHANGED;
                SetBlob(NULL, 0);
            }
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "smsfull,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            // The "smsfull" indication can be usefull for GCF 34.2.3
            if (nValue == 1)
            {
#ifdef SIEMENS_MAGNETO
                DWORD dwState;

                dwState = RIL_SIMSTATUSCHANGED_FULL;
                m_fUnsolicited = TRUE;
                m_dwCode = RIL_NOTIFY_SIMSTATUSCHANGED;
                SetBlob(&dwState, sizeof(DWORD));
#else
                m_fUnsolicited = TRUE;
                m_dwCode = RIL_NOTIFY_DEVSPECIFIC_SIM_MEMORY_FULL;
                SetBlob(NULL, 0);
#endif
                m_fUnsolicited = TRUE;
            }
        }
        fRet = TRUE;
    }
    else if (MatchStringBeginning(rszPointer, "simstatus,", rszPointer))
    {
        if (ParseUInt(rszPointer, FALSE, nValue, rszPointer))
        {
            g_iCurrentSIMStatus = nValue;
            
            if (nValue == 5)
                m_pRilHandle->SignalInternalSIMReady();
        }
        fRet = TRUE;
    }
    else
    {
        // Simply throw away all other +CIEV URCs for now
        fRet = FindRspPostfix(rszPointer, rszPointer);
        if (fRet) {
          // Step back over found "<CR><LF>" which is parsed in ParseNotification() again
          rszPointer = rszPointer - 2;
        }
    }

    Error:
    return fRet;
}

#ifdef RIL_ENABLE_ALS
//
//
//
BOOL CResponse::ParseALSStatusStatus(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseALSStatusStatus);
    LPCSTR szDummy;
    UINT nValue;
    DWORD dwALSStatus;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }
    if (nValue > 0)
    {
        dwALSStatus = nValue-1;     // Siemens: ALS starts with 1; Microsoft: ALS starts with 0
    }
    else
    {
        dwALSStatus = 0;
    }

    // If we didn't find a <cr><lf>, this is not a notification, but a response, which has
    // a different format and which we don't handle here.
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    g_dwALSLine = dwALSStatus; // It was the URC -> incomming call comes from this line

    m_fUnsolicited = FALSE;

    fRet = TRUE;

    Error:
    return fRet;
}
#endif

//
//
//
BOOL CResponse::ParseRegistrationStatus(LPCSTR& rszPointer, DWORD const dwNotifyCode)
{
    //TBD_FUNCTION(CResponse::ParseRegistrationStatus);
    LPCSTR szDummy;
    UINT nValue;
    DWORD dwRegStatus;
    DWORD dwOldRegStatus;
    BOOL fRet = FALSE;
    UINT dwLAC,dwCI;
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    RILLOCATIONINFO sLI;
#endif

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete registration notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }
    if (NUM_REGSTATS > nValue)
    {
        dwRegStatus = g_rgdwRegStats[nValue];
    }
    else
    {
        dwRegStatus = RIL_REGSTAT_UNKNOWN;
    }

    dwLAC = 0;
    dwCI = 0;
    // If we didn't find a <cr><lf>, this is not a notification, but a response, which has
    // a different format and which we don't handle here.
    // e.g. the response looks like "+CREG: 1,1<cr><lf>0<cr>"
    // but the notication looks like: "+CREG: 2<cr><lf>"
    // Exception: if +CREG=2, than there are two extra arguments, we have to ignore. In
    // this case, the 2nd argument starts with a quotation mark.
    if (MatchStringBeginning(rszPointer, ",\"", rszPointer))
    {
        ParseHexUInt(rszPointer, TRUE, dwLAC, rszPointer);
        MatchStringBeginning(rszPointer, "\",\"", rszPointer);
        ParseHexUInt(rszPointer, TRUE, dwCI, rszPointer);
        MatchStringBeginning(rszPointer, "\"", rszPointer);
    }
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    dwOldRegStatus = 0xFFFFFFFF;
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED)
    {
        dwOldRegStatus = g_dwRegStatus;
        g_dwRegStatus = dwRegStatus;
    }
    else if (dwNotifyCode == RIL_NOTIFY_GPRSREGSTATUSCHANGED)
    {
        dwOldRegStatus = g_dwGPRSRegStatus;
        g_dwGPRSRegStatus = dwRegStatus;
        if (g_dwModemType == MODEMTYPE_SIEMENS_MC75 && g_dwModemVersion >= 1900 && g_dwModemVersion <=2001)
        {
            // MC75: the +CGREG: URC shows on some network "Home" while roaming. The +CREG URC
            // shows the correct value.
            if (g_dwGPRSRegStatus == RIL_REGSTAT_HOME && g_dwRegStatus == RIL_REGSTAT_ROAMING)
            {
                g_dwGPRSRegStatus = RIL_REGSTAT_ROAMING;
                dwRegStatus = RIL_REGSTAT_ROAMING;
            }
        }
    }

    m_fUnsolicited = TRUE;
    m_dwCode = NULL;
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING) && dwLAC!=0 && dwCI!=0)
    {
        if (g_fReportLocationUpdateAsRegistrationState || dwOldRegStatus != dwRegStatus)
            m_pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_REGSTATUSCHANGED, dwRegStatus);

        m_dwCode = RIL_NOTIFY_LOCATIONUPDATE;

        sLI.cbSize = sizeof(RILLOCATIONINFO);
        sLI.dwLocationAreaCode = dwLAC;
        sLI.dwCellID = dwCI;
        if (!SetBlob((void*)&sLI, sizeof(RILLOCATIONINFO)))
        {
            goto Error;
        }
    }
    else
    {
#endif
    if (g_fReportLocationUpdateAsRegistrationState || dwOldRegStatus != dwRegStatus)
    {
        m_dwCode = dwNotifyCode;
        if (!SetBlob((void*)&dwRegStatus, sizeof(DWORD)))
        {
            goto Error;
        }
    }
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    }
   if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING) && (dwLAC==0 || dwCI==0))
   {
        // It may happen, that location info is missing in URC. In this case, we have to read them
        // manually and notify the OS about the result.
        CreateThread(NULL, 0, CheckLocationUpdateThreadProc, (LPVOID)m_pRilHandle, 0, 0);
   }
 #endif
#if defined(RIL_NOTIFY_EONS)
    if (m_pRilHandle->GetEons()->GetEONSState() == EONS_EMULATED && dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING) && dwLAC!=0 && dwCI!=0)
    {
        // Emulated EONS only: when the location changed, the EONS name might changed, too.
        // So we notify the OS, so it can check for a change of the name.
        m_pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_EONS, NULL, 0);
    }
#endif

    fRet = TRUE;

    if(g_dwEnableGPRSAttach == 1 && dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED &&
       (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING) &&
       (g_dwGPRSRegStatus != RIL_REGSTAT_HOME && g_dwGPRSRegStatus != RIL_REGSTAT_ROAMING))
    {
        // In case we want an automatic GPRS attach, we have to re-attach to GPRS,
        // when we changed the network operator or lost connection due to other reasons.
        CreateThread(NULL, 0, GPRSReAttachThreadProc, (LPVOID)m_pRilHandle , 0, 0);
    }

#ifdef RIL_USE_CSQ_NOTIFICATIONS
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING))
    {
        // Strange behaviour of the Microsoft MMI: if an RIL_NOTIFY_REGSTATUSCHANGED shows up, the MMI
        // forgets about all signal strength URCs so far, so we have to resend it
        CreateThread(NULL, 0, ReSendSignalThreadProc, (LPVOID)m_pRilHandle, 0, 0);
    }
#endif
#if defined(RIL_SYSTEMTYPE_EDGE)
    if (dwNotifyCode == RIL_NOTIFY_GPRSREGSTATUSCHANGED && g_dwModemType == MODEMTYPE_SIEMENS_MC75 && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)) {
        // MC75 specific: we now have to check if we are attached to GPRS or EDGE (+CGREG: 1 or +CGREG: 5).
        CreateThread(NULL, 0, CheckEDGEStatusThreadProc, (LPVOID)m_pRilHandle, 0, 0);
    }
    if (dwNotifyCode == RIL_NOTIFY_REGSTATUSCHANGED && (g_dwGPRSRegStatus == RIL_REGSTAT_HOME || g_dwGPRSRegStatus == RIL_REGSTAT_ROAMING) && g_dwModemType == MODEMTYPE_SIEMENS_MC75 && (dwRegStatus == RIL_REGSTAT_HOME || dwRegStatus == RIL_REGSTAT_ROAMING)) {
        // In some networks not every cell supports EDGE, so the check for EDGE/GPRS  has to be repeated on
        // every +CREG: 1 or +CREG: 5 URC, since the indicate a cell change.
        CreateThread(NULL, 0, CheckEDGEStatusThreadProc, (LPVOID)m_pRilHandle, 0, 0);
    }
#endif
    Error:
    return fRet;
}

DWORD WINAPI PromptForSimAndRequeueCmdProc(LPVOID lpParameter);

DWORD WINAPI SIMReInsertedThreadProc(LPVOID pVoid)
{
    HANDLE hThread;
    CRilHandle*    pRilHandle;

    pRilHandle = (CRilHandle *)pVoid;
    Sleep(5000);
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
    pRilHandle->StartReadyStateQuery();
    hThread = CreateThread(NULL, 0, PromptForSimAndRequeueCmdProc, (LPVOID)NULL, 0, NULL);
    CloseHandle(hThread);
    return 0;
}

//
//
//
BOOL CResponse::ParseSIMCardStatus(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseSIMCardStatus);
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;
    HANDLE hThread;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete SIM card notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer))
    {
        goto Error;
    }
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    if (nValue == 0 || nValue == 2)    // ^SCKS: 0  -> SIM-Card removed or invalid, so we send the "SIM not accessible" message; ^SCKS: 2 -> SIM-Card broken, same action
    {
        g_dwRegStatus = 0;
        g_rsiused = 0;
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_SIMNOTACCESSIBLE;
        g_iPPLMNBufferPos = PPLMN_BUFFER_NOT_FILLED;
        g_dwSIMLockedState = RIL_LOCKEDSTATE_UNKNOWN;
        ClearGPRSParamCache();
#ifdef RIL_USE_CPIN2_WORKAROUND
        ClearPIN2Secure();
#endif

        if (!SetBlob(NULL, 0))
        {
            goto Error;
        }
        UpdateSIMState(RIL_E_SIMNOTINSERTED);
    }
    else if (nValue == 1)    // ^SCKS: 1  -> we start a re-initialization thread
    {
        RILEQUIPMENTSTATE res;

        hThread = CreateThread(NULL, 0, SIMReInsertedThreadProc, (LPVOID)m_pRilHandle, 0, 0);
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED;
        memset(&res, 0x00, sizeof(RILEQUIPMENTSTATE));
        res.cbSize = sizeof(RILEQUIPMENTSTATE);
        res.dwParams = RIL_PARAM_EQUIPMENTSTATE_READYSTATE;
        res.dwReadyState = RIL_READYSTATE_INITIALIZED;
        if (!SetBlob((void *)&res, sizeof(RILEQUIPMENTSTATE)))
        {
            goto Error;
        }
        CloseHandle(hThread);
    }


    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseTemperatureWarning(LPCSTR& rszPointer, INT &iCause)
{
    //TBD_FUNCTION(CResponse::ParseTemperatureWarning);
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete SIM card notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseIntAsUInt(rszPointer, nValue, rszPointer))
    {
        goto Error;
    }
    if (!ParseRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    iCause = (INT)nValue;

    fRet = TRUE;

    Error:
    return fRet;
}


// Problem: if an +CUSD-URC doesn't contain a DCS, we do not know, if this is
// GSM or UCS2 coded. To solve this problem, we have to make a clever guess.
BOOL CResponse::IsProbablyNotUCS2(LPCSTR szDataString)
{
    BOOL bRet = FALSE;
    UINT i, iLen;

    iLen = strlen(szDataString);
    if (iLen == 0 || (iLen % 4) != 0)
        bRet = TRUE;

    for (i=0; i<iLen && !bRet; i++)
    {
        char szSearch[2];

        szSearch[0] = szDataString[i];
        szSearch[1] = 0;
        if (strstr("0123456789ABCDEFabcdef", szSearch) == NULL)
            bRet = TRUE;
    }
    return bRet;
}

//
//
//
BOOL CResponse::ParseUSSDInfo(LPCSTR& rszPointer, BOOL &fExpectNothing)
{
    //TBD_FUNCTION(CResponse::ParseUSSDInfo);
    LPSTR szDataString = NULL;
    UINT cbDataString;
    UINT cbStruct;
    UINT nStatus;
    UINT nDCS;
    // UINT i;
    // BYTE* pbDataWalk;
    // LPSTR pchDataStringWalk;
    LPCSTR szDummy;
    RILSUPSERVICEDATA* prssd = NULL;
    BOOL fRet = FALSE;
    BOOL bDCSunknown = FALSE;
    BOOL bDCSIsNotUCS2 = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete USSD notification -- no need to parse it
        goto Error;
    }

    // Parse "<status>"
    if (!ParseUIntAndVerifyAbove(rszPointer, TRUE, NUM_SUPSVCDATASTATS, nStatus, rszPointer))
    {
        goto Error;
    }

    if (nStatus == 1 && !MatchStringAnywhere(rszPointer, ">", szDummy))
    {
        // Special case status 1: the URC is followed by a >-sign as a prompt
        // we only pars this URC, if the prompt is included.
        goto Error;
    }

    // Parse ","
    if (!MatchStringBeginning(rszPointer, ",", rszPointer))
    {
        // No data parameter present
        prssd = (RILSUPSERVICEDATA*)AllocBlob(sizeof(RILSUPSERVICEDATA));
        if (!prssd)
        {
            goto Error;
        }
        cbStruct = sizeof(RILSUPSERVICEDATA);
        prssd->dwParams = 0;
    }
    else
    {
        // Parse "<data>,<dcs>"
        // NOTE: we take ownerswhip of allocated szDataString
        if (!ParseUnlimitedString(rszPointer, szDataString, cbDataString, rszPointer))
        {
            goto Error;
        }
        if(!MatchStringBeginning(rszPointer, ",", rszPointer) ||
            !ParseUInt(rszPointer, TRUE, nDCS, rszPointer))
        {
            // default nDCS.  this parameter is supposed to be mandatory but we've
            // seen it missing from otherwise normal USSD responses.
            bDCSunknown = TRUE;
            nDCS = 15;        // The default coding scheme is 15 (= unspecific language)
        }

        // Bug in the MC55: sometimes a DCS of 65535 is displayed, when actually 15 is ment.
        if (nDCS == 65535)
        {
            bDCSunknown = TRUE;
            nDCS = 15;
            bDCSIsNotUCS2 = TRUE;
        }
        else if ((g_dwModemType == MODEMTYPE_SIEMENS_MC55 && g_dwModemVersion >= 2092 && g_dwModemVersion <= 3000) ||
                    (g_dwModemType == MODEMTYPE_SIEMENS_MC56 && g_dwModemVersion >= 2092 && g_dwModemVersion <= 3000))
        {
            // In some versions of the Siemens modules the behavior of the +CUSD-URC changed:
            // old: when UCS2 is activated with AT+CSCS="UCS2", the +CUSD-URC is output in UCS2, but if DCS is unkown, output is in GSM, even when UCS2 is set.
            // new: +CUSD is output according to the DCS value regardless of the UCS2 setting.
            if (((0x00 <= nDCS) && (nDCS <= 0x0f)) || (nDCS == 68) || (nDCS == 64) || (nDCS == 32))
            {
                bDCSIsNotUCS2 = TRUE;
                bDCSunknown = TRUE;
            }
        }

        // See GSM 03.38 for Cell Broadcast DCS details
        // Currently support only default alphabet in all languages (i.e., the DCS range 0x00..0x0f)
        // Also applies to uncompressed default alphabet (DCS=64) and uncompressed 8 bit data (DCS=68)
        // We also support the Czech language version (DCS=32)
        // Since the USSD URC always uses UCS2 on the Siemens driver, we can also treat
        // uncompressed UCS2 messages (DCS=72) here.
        // Only exception: when DCS is unkown. In this case the USSD strings comes as GSM, even when
        // +CSCS is set to "UCS2".
        if (bDCSunknown && (bDCSIsNotUCS2 || IsProbablyNotUCS2(szDataString)))
        {
            // Default alphabet encoding (which has been requested in our init string to allow direct copying of bytes)

            // At this point, cbDataString has the size of the szDataString which ParseUnlimitedString allocated for us and
            // which holds the TE-encoded characters.
            // However, this byte count also includes an extra byte for the NULL character at the end, which we're not going to translate.
            // Therefore, decrement cbDataString by 1
            cbDataString--;

            // Now figure out the number of Unicode characters needed to hold the string after it's converted
            size_t cbUnicode;
            if (!EncodedStringSizeToUnicodeStringSize(ENCODING_TECHARSET, cbDataString, cbUnicode))
            {
                goto Error;
            }

            // cbUnicode now has the number of bytes we need to hold the Unicode string generated when we
            // convert from the TE-encoded string.
            // However, it doesn't include space for the terminating NULL, so we need to allow for that also.
            // (Note that ParseEncodedString will append the terminating NULL for us automatically.)
            cbUnicode += sizeof(TCHAR);

            // Allocate a structure big enough to hold the header and the null-terminated unicode string
            cbStruct = sizeof(RILSUPSERVICEDATA) + cbUnicode;
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
            if (!prssd)
            {
                goto Error;
            }

            // Now convert the TE-encoded string into Unicode in the buffer we just allocated
            if (!ParseEncodedString(ENCODING_TECHARSET,
                                    szDataString,
                                      szDataString + cbDataString,
                                      (LPWSTR)(prssd->pbData),
                                      (LPWSTR)(prssd->pbData + cbUnicode)))
            {
                goto Error;
            }

            prssd->dwParams = RIL_PARAM_SSDI_DATA;
        }
        // We can also handle uncompressed UCS2 data, but we have to convert them
        else if (((0x00 <= nDCS) && (nDCS <= 0x0f)) || (nDCS == 68) || (nDCS == 64) || (nDCS == 32) || (nDCS == 72) || bDCSunknown)
        {
            cbDataString--;
            size_t cbUnicode;
            if (!EncodedStringSizeToUnicodeStringSize(ENCODING_UCS2, cbDataString, cbUnicode))
            {
                goto Error;
            }

            cbUnicode += sizeof(TCHAR);

            // Allocate a structure big enough to hold the header and the null-terminated unicode string
            cbStruct = sizeof(RILSUPSERVICEDATA) + cbUnicode;
            prssd = (RILSUPSERVICEDATA*)AllocBlob(cbStruct);
            if (!prssd)
            {
                goto Error;
            }

            // Now convert the UCS2-encoded string into Unicode in the buffer we just allocated
            if (!ParseEncodedString(ENCODING_UCS2,
                                    szDataString,
                                      szDataString + cbDataString,
                                      (LPWSTR)(prssd->pbData),
                                      (LPWSTR)(prssd->pbData + cbUnicode)))
            {
                goto Error;
            }

            prssd->dwParams = RIL_PARAM_SSDI_DATA;
        }
        else
        {
            goto Error;
        }
    }

    prssd->cbSize = cbStruct;
    prssd->dwStatus = g_rgdwSupSvcDataStats[nStatus];
    prssd->dwParams |= RIL_PARAM_SSDI_STATUS;

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_SUPSERVICEDATA;
    if (!SetBlob((void*)prssd, cbStruct))
    {
        goto Error;
    }
    fRet = TRUE;
    if (g_rgdwSupSvcDataStats[nStatus] == RIL_SUPSVCDATASTATUS_FURTHERINFOREQUIRED)
    {
        // USSD-URC of type RIL_SUPSVCDATASTATUS_FURTHERINFOREQUIRED is followed by an ">" sign:
        MatchStringAnywhere(rszPointer, ">", rszPointer);
        m_pRilHandle->EnterUSSDAnswerMode(m_PortType);
        fExpectNothing = TRUE;
    }

    if (prssd->dwStatus ==  RIL_SUPSVCDATASTATUS_UNSUPPORTED || prssd->dwStatus == RIL_SUPSVCDATASTATUS_TIMEOUT)
    {
        HandleUSSDError();
    }

    Error:
    delete[] szDataString;
    FreeBlob(prssd);
    return fRet;
}

void CResponse::HandleUSSDError()
{
#ifdef RIL_ADVANCEDUSSDERRORCAUSES
    CreateThread(NULL, 0, InvestigateUSSDThreadProc, (LPVOID)m_pRilHandle, 0, 0);
#endif
    if (IS_ADI)
    {
        CreateThread(NULL, 0, CancelUSSDThreadProc, (LPVOID)m_pRilHandle, 0, 0);
    }
}


//
//
//
BOOL CResponse::ParseMessage(LPCSTR& rszPointer, const DWORD dwNotifyCode)
{
    //TBD_FUNCTION(CResponse::ParseMessage);
    LPCSTR szDummy;
    LPCSTR szEndOfPDU;
    UINT nLength;
    UINT cbMessage;
    BOOL fPrependedSMSC = FALSE;
    RILMESSAGE rmMessage;
    HRESULT hr;
    BOOL fRet = FALSE;

    if (MatchStringAnywhere(rszPointer, "\r\n", szDummy) && ((g_dwModemType == MODEMTYPE_SIEMENS_MC75 && g_dwModemVersion >= 1100) || (g_dwModemType == MODEMTYPE_SIEMENS_TC63 && g_dwModemVersion >= 1050)) && dwNotifyCode==RIL_NOTIFY_BCMESSAGE)
    {
        // MC75 Rel1: occasionally there appears an cell broadcast message where PDU and URC are not
        // seperated. To be on the safe side, we ignore them.
        if (szDummy-rszPointer > 5)
        {
            if (FindRspPostfix(rszPointer, rszPointer))
            {
                fRet = TRUE;
                m_fUnsolicited = TRUE;
                m_dwCode = NULL;
                SetBlob(NULL, 0);
                rszPointer -= 2;
                goto Error;
            }
        }
    }

    // Look for a "<CR><LF>" and a "<postfix>"
    if (!MatchStringAnywhere(rszPointer, "\r\n", szDummy) ||
        !FindRspPostfix(szDummy, szEndOfPDU))
    {
        // This isn't a complete message notification -- no need to parse it
        goto Error;
    }

    szEndOfPDU -= 2; // back up over <cr><lf>

    // Parse "<length><CR><LF>"
    if (!ParseUInt(rszPointer, FALSE, nLength, rszPointer) ||
        !MatchStringBeginning(rszPointer, "\r\n", rszPointer))
    {
        // We failed parsing
        ParseJunk(2, rszPointer);
        fRet = TRUE;
        goto Error;
    }

    if (RIL_NOTIFY_BCMESSAGE == dwNotifyCode)
    {
        cbMessage = nLength * 2;
    }
    else
    {
        // Calculate the total number of bytes in the message data: (length of the PDU data +
        //    length os SC address (stored in the first 2 bytes) + 1) * 2

        if ('+' == *rszPointer)
        {
            cbMessage = nLength * 2 + SMS_BOGUS_ADDR_LENGTH;
            fPrependedSMSC = TRUE;
        }
        else
        {
            // Start by assuming the PDU contains the correct number of semi-octets.
            cbMessage = nLength * 2;

            // If the PDU contains more data than expected, then we probably have
            // the SMSC address prepended to the PDU.
            if ((UINT)(szEndOfPDU - rszPointer) > cbMessage)
            {
                cbMessage += (1 + SemiByteCharsToByte(*rszPointer, *(rszPointer + 1))) * 2;
                fPrependedSMSC = TRUE;
            }
        }
    }

    if ((UINT)(szEndOfPDU - rszPointer) < cbMessage)
    {
        // We have a "complete" response but not enough characters
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CResponse::ParseMessage : Module provided too little data for SMS message; unable to parse correctly - discarding"));
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }

    // Parse "<pdu>"
    if (RIL_NOTIFY_BCMESSAGE == dwNotifyCode)
    {
        if (0 == cbMessage)
        {
            cbMessage = szEndOfPDU - rszPointer;
        }
        TBD_ASSERT(0 == cbMessage % 22);    // See GSM 03.41 section 9
        hr = ParseCellBroadcastMessage(rszPointer, cbMessage, rmMessage);
    }
    else
    {
        hr = ParseSMSMessage(rszPointer, cbMessage, TRUE, fPrependedSMSC, rmMessage);
    }
    if (FAILED(hr))
    {
        // We failed parsing
        ParseJunk(1, rszPointer);
        fRet = TRUE;
        goto Error;
    }
    rszPointer += cbMessage;

    m_fUnsolicited = TRUE;
    m_dwCode = dwNotifyCode;
    if (!SetBlob((void*)&rmMessage, sizeof(RILMESSAGE)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseMessageInSim(LPCSTR& rszPointer, const DWORD dwNotifyCode, BOOL fNoNotification)
{
    //TBD_FUNCTION(CResponse::ParseMessageInSim);
    RILMESSAGE_IN_SIM rmMessageInSim;
    BOOL fRet = FALSE;
    LPCSTR szDummy;
    UINT Location;
    UINT Index;

    // Look for a "<postfix>" to be sure we got a whole message
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        goto Error;
    }

    // Look for a "<Location>,<Index>"
    if ( (!ParseGetLocation(rszPointer, Location, rszPointer)) ||
         (!MatchStringBeginning(rszPointer, ",", rszPointer)) ||
         (!ParseUInt(rszPointer, FALSE, Index, rszPointer)))
    {
        goto Error;
    }

    rmMessageInSim.cbSize = sizeof(rmMessageInSim);
    rmMessageInSim.dwParams = RIL_PARAM_MIS_LOCATION | RIL_PARAM_MIS_INDEX;
    rmMessageInSim.dwLocation = Location;
    rmMessageInSim.dwIndex = Index;

    m_fUnsolicited = TRUE;
    if (!fNoNotification)
    {
        m_dwCode = dwNotifyCode;
        if (!SetBlob((void*)&rmMessageInSim, sizeof(rmMessageInSim)))
        {
            goto Error;
        }
    }
    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
BOOL CResponse::ParseSIMToolkitCmdOrRsp(LPCSTR& rszPointer, const DWORD dwNotifyCode)
{
    //TBD_FUNCTION(CResponse::ParseSIMToolkitCmdOrRsp);
    UINT cbDataString;
    LPSTR szDataString = NULL;
    LPSTR pchDataStringWalk;
    UINT cbData;
    BYTE* pbData = NULL;
    BYTE* pbDataWalk;
    LPCSTR szDummy;
    UINT i;
    BOOL fRet = FALSE;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete SIM Toolkit notification -- no need to parse it
        goto Error;
    }

    // Parse "<data>"
    // NOTE: we take ownerswhip of allocated szDataString
    if (!ParseUnlimitedUnquotedString(rszPointer, '\r', szDataString, cbDataString, rszPointer))
    {
        goto Error;
    }

    if ( (szDataString[0]=='\0') && (dwNotifyCode==RIL_NOTIFY_SIMTOOLKITCMD) )
    {
        m_dwCode=RIL_NOTIFY_SIMTOOLKITSESSIONEND;
    }
    else
    {
        TBD_ASSERT(0 == (cbDataString - 1) % 2);

        cbData = (cbDataString - 1) / 2;
        pbData = (BYTE*)AllocBlob(cbData);
        if (!pbData)
        {
            goto Error;
        }

        pbDataWalk = pbData;
        pchDataStringWalk = szDataString;
        for (i = 0; i < cbData; i++)
        {
            *pbDataWalk = SemiByteCharsToByte(*pchDataStringWalk, *(pchDataStringWalk + 1));
            pbDataWalk++;
            pchDataStringWalk += 2;
        }

        if (!SetBlob((void*)pbData, cbData))
        {
            goto Error;
        }
        m_dwCode = dwNotifyCode;
    }

    m_fUnsolicited = TRUE;
    fRet = TRUE;

    Error:
    delete[] szDataString;
    FreeBlob(pbData);
    return fRet;
}


//
//
//
BOOL CResponse::ParseSIMToolkitCallSetup(LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseSIMToolkitCallSetup);
    LPCSTR szDummy;
    UINT nValue;
    BOOL fRet = FALSE;

    // Ignore any blank characters at the beginning
    if (*rszPointer == ' ') rszPointer++;

    // Look for a "<postfix>"
    if (!FindRspPostfix(rszPointer, szDummy))
    {
        // This isn't a complete SIM Toolkit notification -- no need to parse it
        goto Error;
    }

    if (rszPointer + 2 == szDummy)
    {
        //
        // The "<timeout>" value is not specified
        //

        nValue = 0;
    }
    else
    {
        // Parse "<timeout>"
        if (!MatchStringBeginning(rszPointer, "\"", rszPointer) ||
            !ParseUInt(rszPointer, FALSE, nValue, rszPointer)   ||
            !MatchStringBeginning(rszPointer, "\"", rszPointer))
        {
            // Parsing failure
            ParseJunk(1, rszPointer);
            fRet = TRUE;
            goto Error;
        }
    }

    m_fUnsolicited = TRUE;
    m_dwCode = RIL_NOTIFY_SIMTOOLKITCALLSETUP;
    if (!SetBlob((void*)&nValue, sizeof(DWORD)))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
void CResponse::ParseJunk(UINT nCRLFs, LPCSTR& rszPointer)
{
    //TBD_FUNCTION(CResponse::ParseJunk);
    TBD_ASSERT(0 < nCRLFs);
    LPCSTR szPointerTmp = rszPointer;

    UINT i;

    // Try to match all the CRLFs we should have
    for (i = 0; i < nCRLFs; i++)
    {
        if (!MatchStringAnywhere(szPointerTmp, "\r\n", szPointerTmp))
        {
            break;
        }
    }

    // Back up over the last <CR><LF> iff we found one
    if (szPointerTmp > (rszPointer+2))
    {
        rszPointer = szPointerTmp-2;
    }

    m_fUnrecognized = TRUE;

}


//
//
//
BOOL CResponse::ParseOKOrErrorData(CCommand*& rpCmd, BOOL fCmdOk)
{
    //TBD_FUNCTION(CResponse::ParseOKData);
    HRESULT hr;
    BOOL fRet = FALSE;
    BOOL fSuppressLogging = rpCmd->CmdOptIsSet(CMDOPT_SUPPRESSLOGGING);

    if ((fCmdOk && rpCmd->GetParseFunc()) || (!fCmdOk && rpCmd->GetErrorFunc()))
    {
        if (fCmdOk)
        {
#ifdef TBD_DEBUG
            if (m_pBlob || m_cbBlob)
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CResponse::ParseOKOrErrorData : %d bytes of data have already been set for this \"OK\" response"), m_cbBlob);
            }
#endif
            DeleteBlob();
            hr = rpCmd->GetParseFunc()(GetData(), m_pBlob, m_cbBlob, rpCmd->GetParseParam());
        }
        else
        {
            hr = rpCmd->GetErrorFunc()(GetData(), rpCmd->GetParseParam());
        }

        if (FAILED(hr))
        {
#ifdef TBD_DEBUG
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CResponse::ParseOKOrErrorData : Error parsing success response: %s; hr = 0x%x"), VerboseString(TString(GetData())), hr);
#endif
            if (!fSuppressLogging)
            {
                g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_PARSERSPFAILED, PrintableString(GetData(), GetLength()), (DWORD*)&hr);
            }
            else
            {
                g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_PRSRSPFAILEDSUPPRESSED, rpCmd->GetAPIID());
            }
            if (!OKToError(hr))
            {
                goto Error;
            }
        }
        else
        {
            DWORD dwZone = rpCmd->CmdOptIsSet(CMDOPT_POLLING) ?
                RILLOG_ZONE_CMD_POLLING : RILLOG_ZONE_CMD;
            if (!fSuppressLogging)
            {
                g_RilLog.LogEvent(dwZone, RILLOG_EVENT_PARSERSPOK, PrintableString(GetData(), GetLength()));
            }
            else
            {
                g_RilLog.LogEvent(dwZone, RILLOG_EVENT_PRSRSPOKSUPPRESSED, rpCmd->GetAPIID());
            }
        }
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
HRESULT CResponse::GetError() const
{
    //TBD_FUNCTION(CResponse::GetError);
    HRESULT hr = S_OK;

    // Make sure this response is an error
    if (RIL_RESULT_ERROR != m_dwCode)
    {
        goto Error;
    }

    // Make sure the blob contains an HRESULT
    if (sizeof(HRESULT) != m_cbBlob)
    {
        goto Error;
    }

    // Extract the HRESULT
    hr = *(HRESULT*)m_pBlob;

    Error:
    return hr;
}


//
//
//
CNotificationData::CNotificationData()
: m_dwCode(0),
m_pBlob(NULL),
m_cbBlob(0),
m_fInited(FALSE),
m_fDelayedInitFromRsp(FALSE)
{
    // TBD_FUNCTION(CNotificationData::CNotificationData);
}


//
//
//
CNotificationData::~CNotificationData()
{
    // TBD_FUNCTION(CNotificationData::~CNotificationData);
    FreeBlob(m_pBlob);
    m_pBlob = NULL;
    m_cbBlob = 0;
    m_dwCode = 0;
    m_fInited = FALSE;
    m_fDelayedInitFromRsp = FALSE;
}


//
//
//
BOOL CNotificationData::InitFromDWORDBlob(const DWORD dwCode, const DWORD dwBlob)
{
    // TBD_FUNCTION(CNotificationData::InitFromDWORDBlob);
    TBD_ASSERT(NULL == m_pBlob);
    TBD_ASSERT(0 == m_cbBlob);
    TBD_ASSERT(FALSE == !!m_fInited);
    TBD_ASSERT(FALSE == !!m_fDelayedInitFromRsp);

    BOOL fRet = FALSE;

    m_dwCode = dwCode;
    m_cbBlob = sizeof(DWORD);
    m_pBlob = (void*)AllocBlob(m_cbBlob);
    if (!m_pBlob)
    {
        goto Error;
    }
    memcpy(m_pBlob, &dwBlob, m_cbBlob);
    m_fInited = TRUE;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CNotificationData::InitFromRealBlob(const DWORD dwCode, const void* const pBlob, UINT cbBlob)
{
    // TBD_FUNCTION(CNotificationData::InitFromRealBlob);
    TBD_ASSERT(NULL == m_pBlob);
    TBD_ASSERT(0 == m_cbBlob);
    TBD_ASSERT(FALSE == !!m_fInited);
    TBD_ASSERT(FALSE == !!m_fDelayedInitFromRsp);

    BOOL fRet = FALSE;

    m_dwCode = dwCode;
    if (cbBlob)
    {
        m_cbBlob = cbBlob;
        m_pBlob = AllocBlob(m_cbBlob);
        if (!m_pBlob)
        {
            goto Error;
        }
        memcpy(m_pBlob, pBlob, m_cbBlob);
    }
    m_fInited = TRUE;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CNotificationData::FinishInitFromRspBlob(const CResponse& rRsp)
{
    // TBD_FUNCTION(CNotificationData::FinishInitFromRspBlob);
    TBD_ASSERT(NULL == m_pBlob);
    TBD_ASSERT(0 == m_cbBlob);
    TBD_ASSERT(FALSE == !!m_fInited);
    TBD_ASSERT(FALSE != !!m_fDelayedInitFromRsp);

    void* pBlob;
    UINT cbBlob;
    BOOL fRet = FALSE;

    rRsp.GetBlob(pBlob, cbBlob);

    if (cbBlob)
    {
        m_cbBlob = cbBlob;
        m_pBlob = AllocBlob(m_cbBlob);
        if (!m_pBlob)
        {
            goto Error;
        }
        memcpy(m_pBlob, pBlob, m_cbBlob);
    }

    // Turn off delayed init flag
    m_fDelayedInitFromRsp = FALSE;
    m_fInited = TRUE;
    fRet = TRUE;

    Error:
    return fRet;
}


//
//
//
BOOL CNotificationData::DelayInitFromRspBlob(const DWORD dwCode)
{
    // TBD_FUNCTION(CNotificationData::DelayInitFromRspBlob);
    TBD_ASSERT(NULL == m_pBlob);
    TBD_ASSERT(0 == m_cbBlob);
    TBD_ASSERT(FALSE == !!m_fInited);
    TBD_ASSERT(FALSE == !!m_fDelayedInitFromRsp);

    m_dwCode = dwCode;
    m_fDelayedInitFromRsp = TRUE;
    return TRUE;
}
